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://scenes/main.tscn") as PackedScene).instantiate() root.add_child(main) await process_frame await process_frame (main.doc["objects"] as Array).append({ "id": "crate_wood", "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.build_edit_btn.get_parent() == main.bauen_mod, "Bauen-Knopf wohnt im Bauen-Tab") _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) == "bauen" and main.build_panel.visible, "Tab Bauen: Bauen sofort an, Bauteile offen") 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)