From b85e3bed5245fbf5784f99d669a3187c8b86055d Mon Sep 17 00:00:00 2001 From: Hermes godot-vr Date: Sat, 17 Oct 2026 18:24:45 +0000 Subject: [PATCH] =?UTF-8?q?Map-Editor:=20H=C3=B6henlinien=20nur=20im=20Ter?= =?UTF-8?q?rain-Werkzeug=20(t=5F89c0ec95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sly-Wunsch 17.10.: "die Dauersicht stört" — die Konturen sind genial, aber nur im Terrain-Werkzeug (F1) gewünscht. Kopplung am Werkzeug-Wechselpunkt: _set_tool_mode, _after_structure_change und _recheck_terrain_mode eichen die Konturen NACH (effektiv = Nutzer-Fix ODER Terrain-Werkzeug). Kein _process-Poll — die Umschaltung passiert synchron im selben Aufruf, hart ohne Fade (billig und deterministisch). NEU: Taste L (Action editor_contours_toggle; L war sonst unbesetzt — Kollisions-Prüfung im Test) und der Button „≈ Höhenlinien" fixieren die Linien als NUTZER-Wahl dauerhaft in jedem Werkzeug. Default = Fix AUS, Linien erscheinen also nur bei F1. Regression aus dem Fundus gefixt: _after_structure_change baute terrain_view neu OHNE Kontur-Nacheich (das Gitter hatte ihn, die Linien nicht) — nach jedem Strg+Z kehrte die Dauersicht zurück. Der Rebuild bekommt jetzt denselben Nacheich wie das Gitter. Das Relief-Shading (Hillshade + Höhen-Kurve) bleibt davon unberührt und immer an. Auf flachen Maps (< 2 m Relief) schaltet der Shader weiter selbst auf aus. Doku: docs/tasten.md (L-Zeile in Werkzeuge + Kopplungs-Absatz) und H-Overlay-Zeile (help_text.gd). Tests: test_shortcuts +5 Checks (Action registriert, physische Taste L, Kollisions-Freiheit, L ≠ F1/H). test_gui_smoke +21 Checks am ECHTEN main: Start AUS · Undo/Rebuild bleibt AUS (Root-Cause-Regression) · F1 ein → F2/F3/F4 aus (synchron, 0 Frames Poll) · L-Fix an/aus inkl. Button-Sync und Statusmeldungen · Button-Weg ditto. Suite 11/11 ALL OK: ux 137, shortcuts 203, editor_svmap 31, terrain 112, library 94, glb_view 28, spawns 52, testrun 14, bauen 87, gui_smoke 89, rmb_vorrang 32. --- docs/tasten.md | 11 ++++- src/editor/shortcut_keys.gd | 6 +++ src/ui/help_text.gd | 1 + src/ui/main.gd | 59 ++++++++++++++++++++----- tests/test_gui_smoke.gd | 85 +++++++++++++++++++++++++++++++++++++ tests/test_shortcuts.gd | 28 ++++++++++++ 6 files changed, 179 insertions(+), 11 deletions(-) diff --git a/docs/tasten.md b/docs/tasten.md index 64d7128..8587e2b 100644 --- a/docs/tasten.md +++ b/docs/tasten.md @@ -116,13 +116,22 @@ keinen fokussierten Button auslöst. | Esc (beim Greifen) | Greifen abbrechen, alte Positionen zurück | | Esc (beim Platzieren) | Ghost-Preview abbrechen | | Esc (Kurzhilfe offen) | Kurzhilfe schließen | +| **L** | **Höhenlinien fixieren** (Karte t_89c0ec95): Linien immer sichtbar/unsichtbar — unabhängig vom Werkzeug. Ohne Fix erscheinen sie NUR im Terrain-Werkzeug (F1) | -F1–F4 und F5/H wirken NUR, wenn kein Textfeld/SpinBox fokussiert ist und kein +F1–F4 und F5/H/L wirken NUR, wenn kein Textfeld/SpinBox fokussiert ist und kein Dialog offen ist (ShortcutPolicy) — Tippen geht immer vor. Die Terrain-Ziffern 1–4 und [ / ] bleiben (Karte 2/3) NUR im Terrain-Werkzeug belegt — der Werkzeugwechsel läuft jetzt über die F-Tasten, nicht über Ziffern. +Höhenlinien-Kopplung (Karte t_89c0ec95): Die Konturen (1 m, alle 5 m kräftiger) +sind Werkzeug-gekoppelt — **F1 (Terrain) blendet sie ein, jeder andere +Werkzeugwechsel blendet sie aus** (hart, ohne Fade). Wer sie dauerhaft will, +fixiert sie mit **L** oder dem Button „≈ Höhenlinien“ oben. Das Relief-Shading +(Hillshade + Höhen-Kurve) bleibt davon unberührt und immer an. Auf flachen Maps +(< 2 m Gesamtrelief) sind die Linien automatisch aus. L ist sonst unbesetzt +(Kollisions-Prüfung t_89c0ec95). + ## Hilfe & Test (Karte 3/3) | Eingabe | Wirkung | diff --git a/src/editor/shortcut_keys.gd b/src/editor/shortcut_keys.gd index 4c7c069..f8f2eea 100644 --- a/src/editor/shortcut_keys.gd +++ b/src/editor/shortcut_keys.gd @@ -79,6 +79,12 @@ const ACTIONS := { # --- Kurzhilfe-Overlay + Test-Platzhalter -------------------------------- "editor_help_overlay": [KEY_H], "editor_test_map": [KEY_F5], + # --- Höhenlinien fixieren (Karte t_89c0ec95) ------------------------------ + # L = Nutzer-Wahl „Linien immer an/aus" — ohne Fix sind sie NUR im + # Terrain-Werkzeug (F1) sichtbar (Werkzeug-Kopplung in main.gd). L ist + # sonst unbesetzt (Kollisions-Prüfung Karte t_89c0ec95: keine Taste in + # ACTIONS nutzt KEY_L, Ziffern bleiben Terrain-Pinsel allein vorbehalten). + "editor_contours_toggle": [KEY_L], } diff --git a/src/ui/help_text.gd b/src/ui/help_text.gd index 4997282..6b03afa 100644 --- a/src/ui/help_text.gd +++ b/src/ui/help_text.gd @@ -66,6 +66,7 @@ static func text() -> String: ["F4", "Werkzeug: Spawn-Zonen malen"], ["F5", "▶ Karte testen"], ["H", "Diese Kurzhilfe an/aus (Esc schließt auch)"], + ["L", "Höhenlinien FIXIEREN (immer sichtbar/unsichtbar) — ohne Fix erscheinen sie nur im Terrain-Werkzeug (F1)"], ])) out += "[color=#a8b6ad]Vor „Neue Map“ und „Öffnen“ fragt der Editor nach, wenn noch ungespeicherte Änderungen da sind. Die Statuszeile unten zeigt immer das aktive Werkzeug.[/color]\n" return out diff --git a/src/ui/main.gd b/src/ui/main.gd index 7381ca3..046d658 100644 --- a/src/ui/main.gd +++ b/src/ui/main.gd @@ -53,7 +53,11 @@ var redo_btn: Button var grid_btn: Button # „⊞ Gitter“ (Karte t_7ac70014) var contours_btn: Button # „≈ Höhenlinien“ (Karte t_3ebe4086) var _grid_on := false # Nutzer-Wahl für das Maßstabsgitter -var _contours_on := true # Nutzer-Wahl für die Höhenlinien (Karte t_3ebe4086) +# Höhenlinien (Karte t_89c0ec95): _contours_on ist die NUTZER-Wahl „fixiert +# an“ (L-Taste oder Button). Ohne Fix erscheinen die Linien NUR im +# Terrain-Werkzeug (F1) — Sly-Befund 17.10.: dauerhaft sichtbar störte. +# Effektiv = _contours_fix AN ODER tool_mode == "terrain". +var _contours_on := false # Auswahl-Panel var sel_label: Label @@ -220,15 +224,16 @@ func _build_ui() -> void: grid_btn.toggled.connect(_on_grid_toggled) top_h.add_child(grid_btn) - # Höhenlinien-Toggle (Karte t_3ebe4086): Relief-Lesbarkeit — Linien alle - # 1 m plus kräftigere 5-m-Marke. Standardmäßig AN; auf flachen Maps - # (< 2 m Gesamtrelief) schaltet das Terrain-Mesh selbst auf aus. + # Höhenlinien-Button (Karte t_3ebe4086, Kopplung t_89c0ec95): drücken = + # LINIEN FIXIEREN (immer sichtbar, in jedem Werkzeug). Ohne Fix sind die + # Linien NUR im Terrain-Werkzeug (F1) sichtbar; auf flachen Maps (< 2 m + # Gesamtrelief) schaltet das Terrain-Mesh zusätzlich selbst auf aus. contours_btn = Button.new() contours_btn.text = "≈ Höhenlinien" contours_btn.toggle_mode = true - contours_btn.button_pressed = true + contours_btn.button_pressed = false contours_btn.custom_minimum_size = Vector2(150, 40) - contours_btn.tooltip_text = "Höhenlinien (1 m, alle 5 m kräftiger) im Terrain ein-/ausblenden. Bei flachem Terrain (< 2 m) sind sie automatisch aus." + contours_btn.tooltip_text = "Höhenlinien (1 m, alle 5 m kräftiger) FIXIERT an/aus. Ohne Fixierung erscheinen sie nur im Terrain-Werkzeug (F1). Taste L schaltet ebenfalls. Bei flachem Terrain (< 2 m) sind sie automatisch aus." contours_btn.toggled.connect(_on_contours_toggled) top_h.add_child(contours_btn) @@ -1265,6 +1270,14 @@ func _unhandled_key_input(event: InputEvent) -> void: if _tool_key_allowed(): _toggle_help_overlay() get_viewport().set_input_as_handled() + KEY_L: + # Karte t_89c0ec95: Höhenlinien FIXIEREN an/aus — unabhängig vom + # Werkzeug (wer die Linien dauerhaft will, fixiert sie hier). + # Ohne Modifier (kein Strg+L), gleiche Freigabe wie F1–F5/H: + # Tippen in Textfeldern und offene Dialoge gehen vor. + if not ctrl and _tool_key_allowed(): + _on_contours_toggled(not _contours_on) + get_viewport().set_input_as_handled() KEY_G: if ctrl: # Gruppieren/Lösen (Karte t_f6f29d86) @@ -1525,6 +1538,11 @@ func _set_tool_mode(mode: String) -> void: _leave_build_mode() _enter_objects_mode() _update_tool_label() + # Karte t_89c0ec95: Werkzeugwechsel = Höhenlinien-Umschalter — nach dem + # Terrain-Werkzeug (F1) wieder aus, bei Terrain-Eintritt wieder an + # (sofern nicht fixiert). Signal-getrieben an diesem EINEN Wechselpunkt, + # kein per-Frame-Poll. + _sync_contours_to_builder() func _enter_terrain_mode() -> void: @@ -1752,6 +1770,9 @@ func _recheck_terrain_mode() -> void: terrain_panel.visible = false _sync_terrain_button() _update_tool_label() + # Karte t_89c0ec95: Dezenter Terrain-Rückschalter = ebenfalls ein + # Werkzeugwechsel — Linien wieder ausblenden (sofern nicht fixiert). + _sync_contours_to_builder() help_label.text = "Linksklick: auswählen & ziehen · Umschalt+Klick: Auswahl erweitern · Rahmen aufziehen: mehrere wählen · Strg+G: gruppieren\nR: 90° drehen (Mausrad: 15° fein) · Pfeiltasten: 2-m-Raster · G: greifen (Esc: zurück) · Strg+D: duplizieren · Bild↑/↓: anheben/absenken\nStrg+Bild↑: auf Boden setzen · Strg halten: Snap-Zwang · Spiegeln rechts · Entf: löschen · Strg+Z: zurück" _set_status("Terrain-Modus aus: Diese Map passt nicht ins Editor-MVP (Terrain-Auflösung über %d) — die Objekte bleiben normal bearbeitbar." % TerrainField.EDITOR_MAX_RESOLUTION, true) @@ -2062,6 +2083,10 @@ func _after_structure_change() -> void: interaction.call("clear_selection", true) view_builder.call("rebuild", doc, manifest) _sync_grid_to_builder() + # Karte t_89c0ec95: der Rebuild baut terrain_view NEU (Default-Pref „an“) + # — auch die Höhenlinien brauchen ihren Nacheich (Gitter-Muster). Sonst + # kehrte z. B. nach Strg+Z die Dauersicht zurück, die Sly störte. + _sync_contours_to_builder() _refresh_tree() _recheck_terrain_mode() if interaction != null: @@ -2127,21 +2152,35 @@ func _sync_grid_to_builder() -> void: grid_btn.set_pressed_no_signal(_grid_on) -# --- Höhenlinien (Karte t_3ebe4086) ------------------------------------------ +# --- Höhenlinien (Karte t_3ebe4086, Werkzeug-Kopplung t_89c0ec95) ------------ + +## Effektive Höhenlinien-Sichtbarkeit: FIX-Anforderung des Nutzers (L/Button) +## ODER das Terrain-Werkzeug (F1) ist aktiv. Bewusst KEIN _process-Poll — +## der Hook hängt am Werkzeug-Wechselpunkt (_set_tool_mode) und am Button/L. +func _contours_effective() -> bool: + return _contours_on or tool_mode == "terrain" + func _on_contours_toggled(pressed: bool) -> void: _contours_on = pressed _sync_contours_to_builder() + if status_label != null: + _set_status("Höhenlinien %s — %s" % [ + "FIXIERT AN" if _contours_on else "Fix aus", + "jetzt in jedem Werkzeug sichtbar (L oder Button schaltet zurück)." + if _contours_on else + "sichtbar nur noch im Terrain-Werkzeug (F1). L fixiert sie wieder."]) -## Nach jedem Viewport-Rebuild: Nutzer-Wahl wieder anwenden — der Rebuild -## baut das Terrain-Mesh (und damit dessen Shader-Uniform) neu. +## Nach jedem Viewport-Rebuild und nach JEDEM Werkzeug-Wechsel: die effektive +## Wahl anwenden — der Rebuild baut das Terrain-Mesh (und damit dessen +## Shader-Uniform) neu, der Werkzeugwechsel blendet die Linien ein/aus. func _sync_contours_to_builder() -> void: if view_builder == null: return var terrain_view = view_builder.get("terrain_view") if terrain_view != null and is_instance_valid(terrain_view): - terrain_view.call("set_contours", _contours_on) + terrain_view.call("set_contours", _contours_effective()) if contours_btn != null and contours_btn.button_pressed != _contours_on: contours_btn.set_pressed_no_signal(_contours_on) diff --git a/tests/test_gui_smoke.gd b/tests/test_gui_smoke.gd index 368414e..054b092 100644 --- a/tests/test_gui_smoke.gd +++ b/tests/test_gui_smoke.gd @@ -42,6 +42,7 @@ func _run_all() -> void: _test_shortcut_undo() _test_terrain_hotkeys() _test_shortcuts_karte3() + _test_contours_tool_coupling() _finish() @@ -316,3 +317,87 @@ func _test_shortcuts_karte3() -> void: # Aufräumen: Undo, damit spätere Gruppen (falls Testreihenfolge sich ändert) # nicht von diesem Objekt ausgehen. main._unhandled_key_input(_press_key(KEY_Z, true)) + + +## Karte t_89c0ec95: Höhenlinien NUR im Terrain-Werkzeug — Kopplung am +## Werkzeugwechsel (F1 ein, F2/F3/F4 aus), Fix über L / Button. Der Test +## drückt alles SYNCHRON durch _set_tool_mode bzw. _unhandled_key_input: +## die Sichtbarkeit kippt im selben Aufruf — der Beweis, dass kein +## _process-Poll nötig ist (0 Frames). +func _test_contours_tool_coupling() -> void: + print("Testgruppe: Höhenlinien-Werkzeug-Kopplung (t_89c0ec95)") + # Aufräumen aus der karte3-Gruppe: programmisches confirmed.emit() schließt + # das dirty-Dialog-Window NICHT — ein sichtbares Window sperrt das + # F/L-Tasten-Gate (ShortcutPolicy). Deshalb hier alle Windows zu. + for child in main.get_children(): + if child is Window: + child.hide() + main._set_tool_mode("objects") + var terrain_view = main.view_builder.get("terrain_view") + _check(terrain_view != null, "Terrain-View existiert im Builder") + _check(str(main.get("tool_mode")) == "objects", "Start: Objekt-Werkzeug") + _check(not bool(terrain_view.get("_contours_pref")), + "Start: Höhenlinien AUS (kein Fix, kein Terrain-Werkzeug)") + + # Regression zur obigen Root-Cause: Struktur-Änderung (Undo) baut das + # Terrain-Mesh NEU — die Linien dürfen NICHT auf Default „an“ zurück- + # springen (vor t_89c0ec95 geschah genau das nach jedem Strg+Z). + var before_obj: Dictionary = main.history.snapshot(["objects"]) + (main.doc["objects"] as Array).append( + {"id": "test_contour_objekt", "pos": [2.0, 0.0, 2.0], "scale": 1.0, + "rot_y_deg": 0.0}) + main.history.commit("Test: Kontur-Objekt", before_obj, + main.history.snapshot(["objects"])) + main._do_undo() + terrain_view = main.view_builder.get("terrain_view") # Rebuild → neue View + _check(terrain_view != null, "Undo: Terrain-View frisch vorhanden") + _check(not bool(terrain_view.get("_contours_pref")), + "Struktur-Änderung (Undo + Rebuild): Höhenlinien bleiben AUS im Objekt-Werkzeug") + + # F1 → Terrain: Linien EIN, synchron im Wechselpunkt. + main._unhandled_key_input(_press_key(KEY_F1, false)) + _check(str(main.get("tool_mode")) == "terrain", "F1: Terrain-Werkzeug an") + _check(bool(terrain_view.get("_contours_pref")), + "F1: Höhenlinien EIN (synchron, 0 Frames Poll)") + + # Jedes andere Werkzeug: Linien wieder AUS. + main._unhandled_key_input(_press_key(KEY_F2, false)) + _check(str(main.get("tool_mode")) == "objects", "F2: zurück zum Objekt-Werkzeug") + _check(not bool(terrain_view.get("_contours_pref")), "F2: Höhenlinien AUS") + main._unhandled_key_input(_press_key(KEY_F3, false)) + _check(not bool(terrain_view.get("_contours_pref")), "F3 (Bauen): Höhenlinien AUS") + main._unhandled_key_input(_press_key(KEY_F4, false)) + _check(not bool(terrain_view.get("_contours_pref")), "F4 (Spawns): Höhenlinien AUS") + main._unhandled_key_input(_press_key(KEY_F2, false)) + + # L = Fixieren: sichtbar sogar AUSSERHALB des Terrain-Werkzeugs. + main._unhandled_key_input(_press_key(KEY_L, false)) + _check(bool(terrain_view.get("_contours_pref")), + "L: Höhenlinien FIXIERT AN (sichtbar im Objekt-Werkzeug)") + _check(main.contours_btn.button_pressed, + "L: Button „≈ Höhenlinien“ mitgesynced (pressed)") + _check(str(main.status_label.text).contains("FIXIERT"), + "L: Statuszeile meldet Fix AN") + + # Fix bleibt über Werkzeugwechsel bestehen … + main._unhandled_key_input(_press_key(KEY_F1, false)) + _check(bool(terrain_view.get("_contours_pref")), "Fix an: F1 lässt Linien an") + # … aber ein Werkzeugwechsel KANN bei Fix-an nie ausblenden. L im Terrain- + # Werkzeug löst den Fix: Linien bleiben (Werkzeug-Kopplung trägt weiter). + main._unhandled_key_input(_press_key(KEY_L, false)) + _check(not bool(main.get("_contours_on")), "L im Terrain: Fix gelöst") + _check(bool(terrain_view.get("_contours_pref")), + "L im Terrain: Linien bleiben AN (Werkzeug-Kopplung trägt)") + _check(not main.contours_btn.button_pressed, + "L im Terrain: Button-Fix mitgesynced (unpressed)") + main._unhandled_key_input(_press_key(KEY_F2, false)) + _check(not bool(terrain_view.get("_contours_pref")), + "F2 nach Fix-Lösung: Höhenlinien wieder AUS") + + # Button-Weg (ohne L): Drücken des Buttons fixiert ditto. + main.contours_btn.set_pressed(true) # feuert toggled → _on_contours_toggled + _check(bool(terrain_view.get("_contours_pref")), + "Button: Fix AN fixiert Linien im Objekt-Werkzeug") + main.contours_btn.set_pressed(false) + _check(not bool(terrain_view.get("_contours_pref")), + "Button: Fix AUS blendet zurück (nur Terrain-Werkzeug)") diff --git a/tests/test_shortcuts.gd b/tests/test_shortcuts.gd index 8001069..8b0b0b6 100644 --- a/tests/test_shortcuts.gd +++ b/tests/test_shortcuts.gd @@ -14,6 +14,7 @@ func _init() -> void: _test_inputmap_actions() _test_fly_key_conflicts() _test_inputmap_actions_3() + _test_contours_toggle() _test_policy_3() _test_fly_math() _test_esc_policy() @@ -306,6 +307,33 @@ func _test_inputmap_actions_3() -> void: _check(ok, "%s → physische Taste %s" % [action, OS.get_keycode_string(int(f_keys[action]))]) +## Karte t_89c0ec95: L = Höhenlinien fixieren — Action-Registrierung + +## Kollisions-Freiheit (die nackte Taste L wird von keiner Editor-Action +## genutzt; der Haupt-Handler verlangt zusätzlich „ohne Strg“). +func _test_contours_toggle() -> void: + print("Testgruppe: Höhenlinien-Fix (Karte t_89c0ec95, Taste L)") + _check(InputMap.has_action("editor_contours_toggle"), + "Action existiert: editor_contours_toggle") + var l_ok := false + for ev in InputMap.action_get_events("editor_contours_toggle"): + var kev := ev as InputEventKey + if ev is InputEventKey and kev.physical_keycode == KEY_L: + l_ok = true + _check(l_ok, "editor_contours_toggle → physische Taste L") + var collision := false + for action in ShortcutKeys.ACTIONS: + if action == "editor_contours_toggle": + continue + for entry in ShortcutKeys.ACTIONS[action]: + if entry is int and int(entry) == KEY_L: + collision = true + elif entry is Dictionary and int(entry.get("key", KEY_NONE)) == KEY_L: + collision = true + _check(not collision, "Kollisions-Check: L ist sonst UNBESetzt (keine andere Action nutzt KEY_L)") + _check(not _matches(KEY_L, "editor_tool_terrain"), "L löst KEINEN Werkzeugwechsel aus") + _check(not _matches(KEY_L, "editor_help_overlay"), "L löst KEIN H aus") + + ## Karte 3/3: Pure Policy — F-Tasten-Gate (Textfeld/Dialog) + Dirty-Confirm. func _test_policy_3() -> void: print("Testgruppe: ShortcutPolicy Karte 3/3 (F-Tasten-Gate, Dirty-Confirm)")