scopedvr-map-editor/tests/test_ui_aufraeumen.gd
2026-11-01 19:34:05 +01:00

142 lines
6.4 KiB
GDScript

extends SceneTree
## Editor-Test (headless): UI-Aufräumen 25.10. — eigene Tabs Terrain/Malen/Bauen,
## Viewport-Werkzeugleiste, Dreh-/Kipp-Gizmo (alle Achsen, Undo), Kontrast.
## Ausführen: godot --headless --path . --script res://tests/test_ui_aufraeumen.gd
var failures := 0
var checks := 0
var main: Control
func _initialize() -> void:
_run.call_deferred()
func _run() -> void:
main = (load("res://core/scenes/main.tscn") as PackedScene).instantiate()
root.add_child(main)
await process_frame
await process_frame
(main.doc["objects"] as Array).append({
"id": "box_001", "pos": [4.0, 0.0, 4.0], "scale": 1.0, "rot_y_deg": 0.0})
main.call("_after_structure_change")
main.history.clear()
await process_frame
print("== Tabs ==")
var tabs: TabContainer = main.ui_right_panel
var titel: Array = []
for i in tabs.get_tab_count():
titel.append(tabs.get_tab_title(i))
_check(titel.has("Terrain") and titel.has("Malen") and titel.has("Bauen"), "Tabs Terrain, Malen, Bauen vorhanden")
_check(main.malen_mod != null and main.bauen_mod != null, "Module malen/bauen gebaut")
_check(main.terrain_mod.terrain_mode_paint.get_parent() == main.terrain_mod.terrain_panel and not main.terrain_mod.terrain_mode_paint.visible,
"Mal-Modus nicht mehr als Knopf im Terrain-Tab")
_check(main.bauen_mod.bau_buttons.size() >= BauKatalog.KINDS.size(), "Bauen-Tab hat einen Knopf je Bauteil (%d)" % main.bauen_mod.bau_buttons.size())
_check(main.malen_mod.mat_buttons.size() == SvmapIO.MATERIAL_IDS.size(), "Malen: eine Kachel je Material")
print("== Tab-Klick aktiviert das Werkzeug direkt ==")
tabs.current_tab = main.terrain_mod.get_index()
_check(str(main.tool_mode) == "terrain" and not main.terrain_mod.terrain_mode_paint.button_pressed,
"Tab Terrain: Terrain-Werkzeug sofort an (Heben)")
_check(main.terrain_mod.terrain_panel.visible, "Terrain-Regler stehen offen")
tabs.current_tab = main.malen_mod.get_index()
_check(str(main.tool_mode) == "terrain" and main.terrain_mod.terrain_mode_paint.button_pressed,
"Tab Malen: Malen sofort an")
_check(main.malen_mod.malen_panel.visible and main.malen_mod.palette.visible, "Malen: Palette offen")
main.malen_mod.radius_slider.value = 20.0
_check(int(main.terrain_mod.terrain_radius_slider.value) == 20, "Malen-Radius steuert den Pinsel")
tabs.current_tab = main.bauen_mod.get_index()
_check(str(main.tool_mode) == "objects" and tabs.current_tab == main.bauen_mod.get_index() and not main.bauen_mod.bau_buttons.is_empty(), "Tab Bauen: Objekt-Werkzeug aktiv, Bauteile da")
main.call("_set_tool_mode", "objects")
tabs.current_tab = 0
print("== Statuszeile = eine Zeile ==")
_check(main.status_mod is HBoxContainer, "Statuszeile ist waagerecht")
_check(main.status_mod.size.y < 80, "Statuszeile niedrig (%.0f px)" % main.status_mod.size.y)
print("== Lesbarkeit ==")
var muted: Color = main.malen_mod.theme_color("muted")
_check(muted.a > 0.7 and muted.r > 0.9, "muted-Farbe ist hell (nicht Schwarz)")
_check(main.status_mod.stats_objects.get_theme_color("font_color").a > 0.0, "Statuszeile lesbar")
print("== Werkzeugleiste + Dreh-Gizmo ==")
_check(not main.viewport_toolbar.visible, "ohne Auswahl keine Werkzeugleiste")
main.interaction.call("select_object", 0)
await process_frame
await process_frame
_check(main.viewport_toolbar.visible, "mit Auswahl erscheint die Werkzeugleiste")
_check(main.gizmo.mode == "move", "Standard: Verschieben")
main.viewport_toolbar.rotate_btn.pressed.emit()
_check(main.gizmo.mode == "rotate", "Knopf Drehen schaltet den Gizmo um")
var obj: Dictionary = main.doc["objects"][0]
var n0: int = main.history.entries.size()
_check(main.gizmo.demo_rotate_axis("x", 30.0), "Kippen um X")
_check(main.gizmo.demo_rotate_axis("z", -15.0), "Kippen um Z")
_check(main.gizmo.demo_rotate_axis("y", 45.0), "Drehen um Y")
var rot := SvmapIO.objekt_rotation(obj)
_check(is_equal_approx(rot.x, 30.0) and is_equal_approx(fposmod(rot.z, 360.0), 345.0) and is_equal_approx(rot.y, 45.0),
"Euler (%s) = (30, 45, 345)" % str(rot))
_check(main.history.entries.size() == n0 + 3, "jeder Zug = ein Undo-Schritt (%d neu)" % (main.history.entries.size() - n0))
main.call("_do_undo")
obj = main.doc["objects"][0]
_check(is_equal_approx(SvmapIO.objekt_rotation(obj).y, 0.0), "Undo nimmt den Y-Zug zurück")
main.call("_do_undo")
main.call("_do_undo")
obj = main.doc["objects"][0]
_check(SvmapIO.objekt_rotation(obj).is_equal_approx(Vector3.ZERO), "dreimal Undo = Ausgangslage")
print("== Ring per Maus ziehen ==")
obj = main.doc["objects"][0]
var rot0 := SvmapIO.objekt_rotation(obj)
var pos0 := Vector3(float(obj["pos"][0]), float(obj["pos"][1]), float(obj["pos"][2]))
main.camera.set("distance", 9.0)
main.camera.call("focus_on", pos0)
main.interaction.call("select_object", 0)
for i in 4:
await process_frame
var pts: Array = main.gizmo._ring_screen_points("y")
var p1: Vector2 = pts[3]
var p2: Vector2 = pts[9]
_check(p1 != Vector2.INF and main.gizmo.pick_ring(p1) == "y", "Ring Y unter dem Cursor gefunden")
var press := InputEventMouseButton.new()
press.button_index = MOUSE_BUTTON_LEFT
press.pressed = true
press.position = p1
main.gizmo._unhandled_input(press)
var mv := InputEventMouseMotion.new()
mv.position = p2
main.gizmo._unhandled_input(mv)
var rel := InputEventMouseButton.new()
rel.button_index = MOUSE_BUTTON_LEFT
rel.pressed = false
rel.position = p2
main.gizmo._unhandled_input(rel)
obj = main.doc["objects"][0]
var rot1 := SvmapIO.objekt_rotation(obj)
_check(absf(rot1.y - rot0.y) > 1.0, "Ring ziehen dreht das Objekt (%.0f° → %.0f°)" % [rot0.y, rot1.y])
_check(Vector3(float(obj["pos"][0]), float(obj["pos"][1]), float(obj["pos"][2])).is_equal_approx(pos0), "Position bleibt")
_check(main.interaction.no_drag_move, "Drehen-Modus: Klick auf Objekt zieht es nicht weg")
main.viewport_toolbar.move_btn.pressed.emit()
_check(not main.interaction.no_drag_move, "Verschieben-Modus: Ziehen wieder erlaubt")
_check(main.gizmo.mode == "move", "Verschieben schaltet zurück")
var scale_btns := 0
for c in main.viewport_toolbar.find_children("*", "Button", true, false):
if str((c as Button).text).to_lower().contains("skal"):
scale_btns += 1
_check(scale_btns == 0, "kein Skalieren-Werkzeug (Objekte behalten ihre Größe)")
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: ", label)
else:
failures += 1
print(" FAIL: ", label)