extends SceneTree ## Editor-Test (headless): Theme-Konsistenz — Karte t_2336c58e (U7 Feinschliff). ## Ausführen: godot --headless --path . -s res://tests/test_theme.gd ## Exit 0 = alles grün. ## ## Was hier geprüft wird: ## 1. Theme-Ressource: main nutzt theme/editor_theme.tres (EINE Farbquelle), ## Theme-Schrift = UIScale.font_size(). ## 2. Theme-Inhalt: Hover/Fokus/Disabled (Button), Tab-Hover, LineEdit, ## Splitter-Anfassen (grabber_area), Scrollbars, Tooltip + die Farb-Token ## (accent, status_ok/error, muted, field, title, warning). ## 3. KEINE hartkodierten Farben im Code: Suche nach "Color(" in ## core/ui/panels/*.gd, core/ui/main.gd, core/ui/inspector_panel.gd. ## 4. Alle Knöpfe (Button/MenuButton/CheckBox) haben einen Tooltip. ## 5. Typografie-Raster: Überschrift > Body > Nebentext, alles über UIScale. ## 6. Fenstertitel zeigt ungespeicherte Änderungen an (U7 Kleinigkeit). const UIScale := preload("res://core/ui/ui_scale.gd") var failures := 0 var checks := 0 var main: Control func _initialize() -> void: _run_all.call_deferred() func _check(cond: bool, label: String) -> void: checks += 1 if cond: print(" ok: %s" % label) else: failures += 1 print(" FAIL: %s" % label) 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 _run_all() -> void: var scene: PackedScene = load("res://core/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_theme_set() _test_theme_content() _test_no_hardcoded_colors() _test_tooltips() _test_typography() _test_window_title_dirty() _finish() ## 1) main hängt genau die Theme-Datei aus dem Projekt an (eine Quelle). func _test_theme_set() -> void: print("Testgruppe: Theme-Ressource gesetzt") _check(main.theme != null, "main.theme gesetzt") if main.theme == null: return var theme_file: Theme = load("res://core/theme/editor_theme.tres") _check(main.theme == theme_file, "main nutzt theme/editor_theme.tres (eine Farbquelle)") _check(int(main.theme.default_font_size) == UIScale.font_size(), "Theme-Schrift = UIScale.font_size() (%d)" % UIScale.font_size()) ## 2) Die U7-Theme-Bausteine sind alle da (Hover/Fokus/Disabled, Splitter, ## Scrollbars, Tooltip, Farb-Token). func _test_theme_content() -> void: print("Testgruppe: Theme-Inhalt (Hover/Fokus/Disabled/Splitter/Farben)") var th: Theme = main.theme _check(th.has_stylebox("disabled", "Button"), "Button: Disabled-Style (ausgegraut)") _check(th.has_stylebox("hover", "Button"), "Button: Hover-Style") _check(th.has_stylebox("pressed", "Button"), "Button: Aktiv-Style") _check(th.has_stylebox("focus", "Button"), "Button: Fokus-Rahmen") _check(th.has_color("font_disabled_color", "Button"), "Button: Disabled-Schriftfarbe (sichtbar ausgegraut)") _check(th.has_color("font_placeholder_color", "LineEdit"), "LineEdit: Platzhalter-Farbe") _check(th.has_stylebox("focus", "LineEdit"), "LineEdit: Fokus-Rahmen") _check(th.has_stylebox("tab_hovered", "TabContainer"), "Tabs: Hover-Zustand") _check(th.has_stylebox("grabber_area", "HSplitContainer"), "Splitter: Anfassen-Fläche (HSplit)") _check(th.has_stylebox("grabber_area", "VSplitContainer"), "Splitter: Anfassen-Fläche (VSplit)") _check(th.has_stylebox("grabber_area_highlight", "HSplitContainer"), "Splitter: Anfassen-Hervorhebung (HSplit)") _check(th.has_stylebox("grabber", "VScrollBar"), "ScrollBar: Griff (V)") _check(th.has_stylebox("grabber", "HScrollBar"), "ScrollBar: Griff (H)") _check(th.has_stylebox("panel", "TooltipPanel"), "Tooltip: Panel-Style") _check(th.has_color("accent", "Editor"), "Farb-Token: accent") _check(th.has_color("status_ok", "Editor"), "Farb-Token: status_ok") _check(th.has_color("status_error", "Editor"), "Farb-Token: status_error") _check(th.has_color("muted", "Editor"), "Farb-Token: muted") _check(th.has_color("field", "Editor"), "Farb-Token: field") _check(th.has_color("title", "Editor"), "Farb-Token: title") _check(th.has_color("warning", "Editor"), "Farb-Token: warning") ## 3) Keine Farbwerte mehr im Code — alles kommt aus dem Theme. ## Scan: core/ui/panels/*.gd (verbindlich) + main.gd + inspector_panel.gd. ## Achtung: nur "Color(" (Konstruktor) zählt — theme_color()/-> Color: sind ## Schlüsselwörter, keine Farbwerte. func _test_no_hardcoded_colors() -> void: print("Testgruppe: keine hartkodierten Farben im Code") var dateien: Array = [] var dir := DirAccess.open("res://core/ui/panels") if dir != null: dir.list_dir_begin() var fname := dir.get_next() while not fname.is_empty(): if fname.ends_with(".gd"): dateien.append("res://core/ui/panels/" + fname) fname = dir.get_next() dateien.append("res://core/ui/main.gd") dateien.append("res://core/ui/inspector_panel.gd") _check(dateien.size() > 5, "Farb-Scan: %d Skripte gefunden" % dateien.size()) var farb_dateien := 0 for pfad in dateien: var text := FileAccess.get_file_as_string(pfad) if text.contains("Color("): farb_dateien += 1 print(" hartkodierte Farbe in: %s" % pfad) _check(farb_dateien == 0, "kein 'Color(' in den Panel-Skripten/main.gd/Inspektor") ## 4) Jeder klickbare Knopf (Button/MenuButton/CheckBox/CheckButton) im ## gebauten UI trägt einen Tooltip — Werkzeugname/Tastenkürzel inklusive. func _test_tooltips() -> void: print("Testgruppe: alle Knöpfe haben Tooltips") var ohne := 0 var mit := 0 var stack: Array = [main] while not stack.is_empty(): var n: Node = stack.pop_back() if n is Button or n is MenuButton: var str_text := str((n as Control).tooltip_text) if str_text.strip_edges().is_empty(): ohne += 1 var ort := str(n.get_parent().name) var elter := n.get_parent() while elter != null and elter != main: elter = elter.get_parent() if elter != null: ort = str(elter.name) + "/" + ort print(" ohne Tooltip: %s (%s) text='%s' pfad=%s" % [ n.name, n.get_class(), str((n as Control).get("text")), ort]) else: mit += 1 for c in n.get_children(): stack.append(c) _check(mit > 20, "Knöpfe mit Tooltip gefunden (%d)" % mit) _check(ohne == 0, "kein Knopf ohne Tooltip (%d ohne)" % ohne) ## 5) Genau drei Typografie-Ebenen über UIScale: Überschrift > Body > Nebentext. func _test_typography() -> void: print("Testgruppe: Typografie-Raster über UIScale") _check(UIScale.heading_font_size() > UIScale.font_size(), "Überschrift (%d) größer als Body (%d)" % [ UIScale.heading_font_size(), UIScale.font_size()]) _check(UIScale.font_size() > UIScale.small_font_size(), "Body (%d) größer als Nebentext (%d)" % [ UIScale.font_size(), UIScale.small_font_size()]) # Stufen-Skalierung wandert mit (Groß-Stufe, ohne UI-Rebuild zu brauchen): UIScale.set_step_index(2) _check(UIScale.heading_font_size() == UIScale.px(UIScale.HEADING_FONT_SIZE), "Überschrift skaliert mit der UI-Stufe (Groß)") _check(UIScale.small_font_size() == UIScale.px(UIScale.SMALL_FONT_SIZE), "Nebentext skaliert mit der UI-Stufe (Groß)") UIScale.set_step_index(0) # zurück auf Klein (wie der Test-Start) ## 6) Fenstertitel nennt ungespeicherte Änderungen (U7-Kleinigkeit). func _test_window_title_dirty() -> void: print("Testgruppe: Fenstertitel bei ungespeicherten Änderungen") main.dirty = true main.call("_refresh_file_label") var titel_dirty := str(root.title) _check(titel_dirty.contains("ungespeichert"), "Titel bei dirty nennt „ungespeichert“ (%s)" % titel_dirty) main.dirty = false main.call("_refresh_file_label") var titel_sauber := str(root.title) _check(not titel_sauber.contains("ungespeichert"), "Titel ohne dirty bleibt sauber (%s)" % titel_sauber)