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.
403 lines
19 KiB
GDScript
403 lines
19 KiB
GDScript
extends SceneTree
|
||
## Editor-Test (headless): GUI-Smoke — Karte t_d3b93793.
|
||
## Ausführen: godot --headless --path . --script res://tests/test_gui_smoke.gd
|
||
## Exit 0 = alles grün.
|
||
##
|
||
## ANDERS als die übrigen Tests instanziert dieser Test die ECHTE Main-UI
|
||
## (scenes/main.tscn → src/ui/main.gd) und deckt damit Runtime-UI-Pfade ab,
|
||
## die die logik-only-Suite nie sieht (GUI-Start-Befund 15.10.: Terrain-Preset-
|
||
## Crash beim Panel-Bau + `command_pressed`-Spam bei jeder Taste — beides
|
||
## headless unsichtbar, weil das UI nie instanziert wurde).
|
||
##
|
||
## Was hier geprüft wird:
|
||
## 1. Panel-Bau überlebt _ready (kein abgebrochener _build_right_panel).
|
||
## 2. Terrain-Größen-Presets sind initial korrekt belegt (Mittel = 12 m)
|
||
## und klickbar (Klick stellt Radius-Slider + Label um).
|
||
## 3. Keyboard-Shortcut-Pfad (_unhandled_key_input) läuft ohne
|
||
## Property-Fehler und setzt Strg+Z wirklich in Undo um.
|
||
|
||
var failures := 0
|
||
var checks := 0
|
||
var main: Control
|
||
|
||
|
||
func _initialize() -> void:
|
||
_run_all.call_deferred()
|
||
|
||
|
||
func _run_all() -> void:
|
||
var scene: PackedScene = load("res://scenes/main.tscn")
|
||
_check(scene != null, "scenes/main.tscn laedt")
|
||
if scene == null:
|
||
_finish()
|
||
return
|
||
main = scene.instantiate()
|
||
root.add_child(main)
|
||
# Zwei Frames: _ready + Layout laufen lassen (auch deferred UI-Arbeit).
|
||
await process_frame
|
||
await process_frame
|
||
|
||
_test_ui_built()
|
||
_test_terrain_presets()
|
||
_test_shortcut_undo()
|
||
_test_terrain_hotkeys()
|
||
_test_shortcuts_karte3()
|
||
_test_contours_tool_coupling()
|
||
|
||
_finish()
|
||
|
||
|
||
func _finish() -> void:
|
||
if failures == 0:
|
||
print("ALL OK: %d checks" % checks)
|
||
quit(0)
|
||
else:
|
||
print("FAILED: %d/%d checks" % [failures, checks])
|
||
quit(1)
|
||
|
||
|
||
func _check(cond: bool, label: String) -> void:
|
||
checks += 1
|
||
if cond:
|
||
print(" ok: %s" % label)
|
||
else:
|
||
failures += 1
|
||
print(" FAIL: %s" % label)
|
||
|
||
|
||
## Abnahme 1: Der komplette UI-Bau (_ready → _build_ui → _build_right_panel)
|
||
## läuft durch — genau dort crashte der Terrain-Preset-Handler (main.gd:659).
|
||
func _test_ui_built() -> void:
|
||
print("Testgruppe: UI-Bau überlebt _ready")
|
||
_check(main != null, "Main-Instanz existiert")
|
||
_check(main.terrain_size_btn_small != null, "Preset-Button Klein gebaut")
|
||
_check(main.terrain_size_btn_medium != null, "Preset-Button Mittel gebaut")
|
||
_check(main.terrain_size_btn_large != null, "Preset-Button Groß gebaut")
|
||
_check(main.terrain_radius_slider != null, "Radius-Slider gebaut")
|
||
_check(main.undo_btn != null and main.redo_btn != null,
|
||
"Undo/Redo-Buttons gebaut (Panel-Bau lief komplett durch)")
|
||
|
||
|
||
## Abnahme 2a: Initialzustand ohne Nebeneffekt-Callback (set_pressed_no_signal):
|
||
## Mittel aktiv, Slider passend 12 m.
|
||
func _test_terrain_presets() -> void:
|
||
print("Testgruppe: Terrain-Größen-Presets")
|
||
_check(main.terrain_size_btn_medium.button_pressed,
|
||
"Mittel ist initial aktiv")
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 12.0),
|
||
"Slider startet bei 12 m (passend zum aktiven Preset)")
|
||
|
||
# Klick auf Groß: button_pressed=true feuert toggled → Handler muss
|
||
# jetzt (mit existierendem Slider) Radius + Label setzen.
|
||
main.terrain_size_btn_large.button_pressed = true
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 32.0),
|
||
"Klick auf Groß setzt Slider auf 32 m")
|
||
_check(str(main.terrain_radius_label.text).contains("32"),
|
||
"Pinsel-Label zeigt 32 m")
|
||
_check(main.terrain_size_btn_medium.button_pressed == false,
|
||
"ButtonGroup: Mittel beim Klick auf Groß deaktiviert")
|
||
|
||
# Klick auf Klein (Position-Klick simuliert echten User-Klick).
|
||
main.terrain_size_btn_small.button_pressed = true
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 4.0),
|
||
"Klick auf Klein setzt Slider auf 4 m")
|
||
|
||
|
||
func _press_key(keycode: Key, ctrl: bool, shift := false) -> InputEventKey:
|
||
var ev := InputEventKey.new()
|
||
ev.keycode = keycode
|
||
ev.physical_keycode = keycode
|
||
ev.pressed = true
|
||
ev.ctrl_pressed = ctrl
|
||
ev.shift_pressed = shift
|
||
return ev
|
||
|
||
|
||
## Abnahme 2b + 3: Der Shortcut-Pfad muss ohne Property-Fehler durchlaufen
|
||
## (vorher crashte `event.command_pressed` bei JEDER Taste) und Strg+Z
|
||
## tatsächlich in ein Undo übersetzen.
|
||
func _test_shortcut_undo() -> void:
|
||
print("Testgruppe: Keyboard-Shortcuts (Strg+Z)")
|
||
# Echte History-Aktion in die laufende Editor-History legen.
|
||
var before: Dictionary = main.history.snapshot(["objects"])
|
||
(main.doc["objects"] as Array).append(
|
||
{"id": "test_objekt", "pos": [10.0, 0.0, 10.0], "scale": 1.0,
|
||
"rot_y_deg": 0.0})
|
||
main.history.commit("Test: Objekt platzieren", before,
|
||
main.history.snapshot(["objects"]))
|
||
_check((main.doc["objects"] as Array).size() == 1, "Test-Objekt platziert")
|
||
|
||
# Karte 1 (t_c46d31c2) hat F belegt: F zentriert die Auswahl — ohne
|
||
# Auswahl gibt es den freundlichen Hinweis (kein Crash, kein Wirken).
|
||
main._unhandled_key_input(_press_key(KEY_F, false))
|
||
_check(str(main.status_label.text).contains("Nichts ausgewählt"),
|
||
"F ohne Auswahl: freundlicher Hinweis, läuft sauber durch")
|
||
|
||
main._unhandled_key_input(_press_key(KEY_Z, true))
|
||
_check((main.doc["objects"] as Array).is_empty(), "Strg+Z hat das Objekt entfernt (Undo wirkt)")
|
||
_check(str(main.status_label.text).contains("Rückgängig gemacht"),
|
||
"Statusleiste meldet das Undo")
|
||
|
||
main._unhandled_key_input(_press_key(KEY_Y, true))
|
||
_check((main.doc["objects"] as Array).size() == 1, "Strg+Y stellt wieder her (Redo wirkt)")
|
||
|
||
# Leere History + Strg+Z: freundliche Meldung statt Crash.
|
||
# Erst das Redo-Ergebnis wieder weg-undoen, DANN ist die History leer.
|
||
main._unhandled_key_input(_press_key(KEY_Z, true))
|
||
_check((main.doc["objects"] as Array).is_empty(), "Strg+Z nach Redo: History leer")
|
||
main._unhandled_key_input(_press_key(KEY_Z, true))
|
||
_check(str(main.status_label.text).contains("Nichts zum Rückgängigmachen"),
|
||
"Strg+Z bei leerer History: Statusmeldung, kein Absturz")
|
||
|
||
|
||
## Karte 2/3 (t_55257792): Terrain-Ziffern 1–4 + [ / ] am ECHTEN main —
|
||
## Buttons/Slider/Label müssen mitwandern, Ziffern außerhalb des Terrain-
|
||
## Werkzeugs dürfen NICHTS tun (Konfliktfreiheit).
|
||
func _test_terrain_hotkeys() -> void:
|
||
print("Testgruppe: Terrain-Pinsel per Tastatur (1–4, [ und ])")
|
||
main._set_tool_mode("terrain")
|
||
_check(str(main.get("tool_mode")) == "terrain", "Terrain-Modus aktiv")
|
||
|
||
# Ziffer 2 = Senken: Modus-Button + Pinsel-Modus wandern mit.
|
||
main._unhandled_key_input(_press_key(KEY_2, false))
|
||
_check(main.terrain_mode_lower.button_pressed, "Ziffer 2: Button „Senken“ aktiv")
|
||
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.LOWER,
|
||
"Ziffer 2: Pinsel-Modus = Senken")
|
||
# Ziffer 4 = Malen: Palette sichtbar, Modus wirklich PAINT (alter
|
||
# set_brush_mode-Clamp hätte das still zu Glätten degradiert).
|
||
main._unhandled_key_input(_press_key(KEY_4, false))
|
||
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.PAINT,
|
||
"Ziffer 4: Pinsel-Modus = Malen (Clamp-Fix)")
|
||
_check(main.terrain_palette.visible, "Ziffer 4: Material-Palette sichtbar")
|
||
# Ziffer 1 zurück: Palette verschwindet wieder.
|
||
main._unhandled_key_input(_press_key(KEY_1, false))
|
||
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.RAISE,
|
||
"Ziffer 1: Pinsel-Modus = Heben")
|
||
_check(not main.terrain_palette.visible, "Ziffer 1: Material-Palette weg")
|
||
|
||
# [ / ] : Preset-Zyklus Klein(4)/Mittel(12)/Groß(32), Slider + Label mit.
|
||
# Slider steht nach dem Klein-Klick oben noch auf 4 m — die Kette startet
|
||
# also von Klein: ] ] ] = 12 → 32 → wrap 4, [ = 32, [ [ = 12 → 4.
|
||
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 12.0),
|
||
"] ab 4 m: Slider auf 12 m (Mittel)")
|
||
_check(str(main.terrain_radius_label.text).contains("12"), "]: Label zeigt 12 m")
|
||
_check(main.terrain_size_btn_medium.button_pressed, "]: Preset-Button Mittel aktiv")
|
||
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 32.0),
|
||
"] ab 12 m: Slider auf 32 m (Groß)")
|
||
_check(str(main.terrain_radius_label.text).contains("32"), "]: Label zeigt 32 m")
|
||
_check(main.terrain_size_btn_large.button_pressed, "]: Preset-Button Groß aktiv")
|
||
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 4.0),
|
||
"] ab 32 m: Zyklus wrappt auf Klein (4 m)")
|
||
main._unhandled_key_input(_press_key(KEY_BRACKETLEFT, false))
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 32.0),
|
||
"[ ab 4 m: wrappt zurück auf Groß (32 m)")
|
||
main._unhandled_key_input(_press_key(KEY_BRACKETLEFT, false))
|
||
main._unhandled_key_input(_press_key(KEY_BRACKETLEFT, false))
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), 4.0),
|
||
"[ zweimal ab 32 m: 12 → 4 (Klein)")
|
||
|
||
# Konfliktfreiheit: außerhalb des Terrain-Werkzeugs tun Ziffern/[ NICHTS.
|
||
main._set_tool_mode("objects")
|
||
var slider_vorher: float = main.terrain_radius_slider.value
|
||
main._unhandled_key_input(_press_key(KEY_2, false))
|
||
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
|
||
_check(is_equal_approx(float(main.terrain_radius_slider.value), slider_vorher),
|
||
"Ziffern/[ im Objekt-Modus: Pinsel bleibt unangetastet")
|
||
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.RAISE,
|
||
"Ziffern im Objekt-Modus: Pinsel-Modus bleibt unangetastet")
|
||
|
||
|
||
## Karte 3/3 (t_11b5c25b): F1–F4 Werkzeugwechsel, F5 Test, H-Hilfe,
|
||
## Strg+O/N/S + Dirty-Dialog am ECHTEN main (Dialoge als --mock: sichtbar
|
||
## prüfen statt bedienen — headless gibt es keine echte Maus).
|
||
func _test_shortcuts_karte3() -> void:
|
||
print("Testgruppe: Karte 3/3 — F-Tasten, Hilfe, Datei, Dirty-Dialog")
|
||
|
||
# --- F1–F4: Werkzeugwechsel (Slys Weiche: F-Tasten statt Ziffern) --------
|
||
main._set_tool_mode("objects")
|
||
main._unhandled_key_input(_press_key(KEY_F1, false))
|
||
_check(str(main.get("tool_mode")) == "terrain", "F1: Terrain-Werkzeug aktiv")
|
||
_check(str(main.tool_label.text).contains("Terrain"), "Statuszeile: Werkzeug-Label zeigt Terrain")
|
||
main._unhandled_key_input(_press_key(KEY_F2, false))
|
||
_check(str(main.get("tool_mode")) == "objects", "F2: zurück zum Objekt-Werkzeug")
|
||
main._unhandled_key_input(_press_key(KEY_F3, false))
|
||
_check(str(main.get("tool_mode")) == "bauen", "F3: Bauen-Werkzeug aktiv")
|
||
_check(str(main.tool_label.text).contains("Bauen"), "Statuszeile: Werkzeug-Label zeigt Bauen")
|
||
main._unhandled_key_input(_press_key(KEY_F4, false))
|
||
_check(str(main.get("tool_mode")) == "spawns", "F4: Spawn-Werkzeug aktiv")
|
||
main._unhandled_key_input(_press_key(KEY_F2, false))
|
||
_check(str(main.get("tool_mode")) == "objects", "F2 aus dem Spawn-Werkzeug: sauber zurück")
|
||
|
||
# --- Fokus-Regel: Textfeld fokussiert → F-Tasten wirken NICHT ------------
|
||
main.prop_name.grab_focus()
|
||
main._unhandled_key_input(_press_key(KEY_F1, false))
|
||
_check(str(main.get("tool_mode")) == "objects",
|
||
"F1 bei fokussiertem Textfeld: Werkzeug bleibt (Tippen geht vor)")
|
||
main.prop_name.release_focus()
|
||
main._unhandled_key_input(_press_key(KEY_F1, false))
|
||
_check(str(main.get("tool_mode")) == "terrain", "F1 nach Release-Focus: wirkt wieder")
|
||
main._set_tool_mode("objects")
|
||
|
||
# --- Dialog-Regel: offener Dialog → F-Tasten wirken NICHT ----------------
|
||
main.error_dialog.popup_centered()
|
||
main._unhandled_key_input(_press_key(KEY_F1, false))
|
||
_check(str(main.get("tool_mode")) == "objects",
|
||
"F1 bei offenem Dialog: Werkzeug bleibt")
|
||
main.error_dialog.hide()
|
||
|
||
# --- H: Kurzhilfe-Overlay an/aus (Esc schließt ebenfalls) ----------------
|
||
main._unhandled_key_input(_press_key(KEY_H, false))
|
||
_check(main.help_overlay.visible, "H: Kurzhilfe-Overlay sichtbar")
|
||
var help_txt: String = main.help_overlay_text.text
|
||
_check(help_txt.contains("Strg+Z") and help_txt.contains("F1") and help_txt.contains("Terrain"),
|
||
"Overlay enthält die Tastenliste aus docs/tasten.md")
|
||
_check(str(main.status_label.text).contains("Kurzhilfe an"), "Statuszeile meldet Overlay an")
|
||
main._unhandled_key_input(_press_key(KEY_ESCAPE, false))
|
||
_check(not main.help_overlay.visible, "Esc schließt das Overlay")
|
||
_check(str(main.get("tool_mode")) == "objects", "Esc am Overlay: Objekt-Werkzeug unangetastet")
|
||
main._unhandled_key_input(_press_key(KEY_H, false))
|
||
main._unhandled_key_input(_press_key(KEY_H, false))
|
||
_check(not main.help_overlay.visible, "H zweimal: Overlay wieder zu (Toggle)")
|
||
|
||
# --- F5: Karte testen (echter Flow existiert — hier läuft er in den
|
||
# „Client nicht gefunden“-Zweig, Hauptsache die Route ist verdrahtet) -----
|
||
main._unhandled_key_input(_press_key(KEY_F5, false))
|
||
_check(str(main.status_label.text).contains("Karte testen"),
|
||
"F5: Karte-testen-Flow angesprungen (Statuszeile meldet)")
|
||
_check(main.error_dialog.visible, "F5: Fehler-Dialog im nicht-gefunden-Zweig geöffnet")
|
||
main.error_dialog.hide()
|
||
|
||
# --- Dirty-Schutz: Strg+N bei ungespeicherten Änderungen FRAGT ZUERST ----
|
||
main.dirty = true
|
||
main._unhandled_key_input(_press_key(KEY_N, true))
|
||
_check(main.dirty_dialog.visible, "Strg+N bei dirty: Bestätigungs-Dialog ZUERST")
|
||
_check(not main.new_dialog.visible, "Strg+N bei dirty: Neue-Map-Dialog wartet hinterher")
|
||
_check(str(main.dirty_dialog.dialog_text).contains("ungespeicherte Änderungen"),
|
||
"Dirty-Dialog: deutsche Klartext-Meldung")
|
||
# „Verwerfen“ (confirmed) → jetzt erst geht der Neue-Map-Dialog auf.
|
||
main.dirty_dialog.confirmed.emit()
|
||
_check(main.new_dialog.visible, "Verwerfen bestätigt: Neue-Map-Dialog öffnet jetzt")
|
||
main.new_dialog.hide()
|
||
main.dirty = false
|
||
|
||
# --- Strg+O ohne dirty: Öffnen-Dialog DIREKT -----------------------------
|
||
main._unhandled_key_input(_press_key(KEY_O, true))
|
||
_check(main.open_dialog.visible, "Strg+N/Strg+O ohne dirty: Öffnen-Dialog direkt")
|
||
main.open_dialog.hide()
|
||
|
||
# --- Strg+S: speichert an bekannten Pfad, dirty wird aufgeräumt ----------
|
||
main.current_path = "/tmp/svmap_karte3_test.svmap"
|
||
main.dirty = true
|
||
main._unhandled_key_input(_press_key(KEY_S, true))
|
||
_check(FileAccess.file_exists("/tmp/svmap_karte3_test.svmap"), "Strg+S: .svmap geschrieben")
|
||
_check(not main.dirty, "Strg+S: dirty zurückgesetzt")
|
||
_check(str(main.status_label.text).contains("gespeichert"), "Strg+S: Statuszeile meldet Speichern")
|
||
|
||
# --- Strg+Umschalt+S: Speichern-unter-Dialog ------------------------------
|
||
main._unhandled_key_input(_press_key(KEY_S, true, true))
|
||
_check(main.save_dialog.visible, "Strg+Umschalt+S: Speichern-unter-Dialog")
|
||
main.save_dialog.hide()
|
||
|
||
# --- Strg+Umschalt+Z: der zweite Redo-Weg --------------------------------
|
||
var before: Dictionary = main.history.snapshot(["objects"])
|
||
(main.doc["objects"] as Array).append(
|
||
{"id": "test_redo_objekt", "pos": [4.0, 0.0, 4.0], "scale": 1.0,
|
||
"rot_y_deg": 0.0})
|
||
main.history.commit("Test: Redo-Objekt", before, main.history.snapshot(["objects"]))
|
||
main._unhandled_key_input(_press_key(KEY_Z, true))
|
||
_check((main.doc["objects"] as Array).size() == 0, "Strg+Z: Objekt weg (Undo)")
|
||
main._unhandled_key_input(_press_key(KEY_Z, true, true))
|
||
_check((main.doc["objects"] as Array).size() == 1, "Strg+Umschalt+Z: Redo-Weg 2 stellt her")
|
||
main._unhandled_key_input(_press_key(KEY_Z, true))
|
||
main._unhandled_key_input(_press_key(KEY_Y, true))
|
||
_check((main.doc["objects"] as Array).size() == 1, "Strg+Y: Redo-Weg 1 stellt auch her")
|
||
# 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)")
|