UI pass: medieval-warm Theme + real Build drawer thumbnails

Theme (scenes/ui/medieval_theme.gd):
- Procedural builder for an app-wide Theme: parchment buttons on
  dark-wood border, tan panels, ink text, gold focus ring.
- Applied on the root Window in Main._ready, with a tree-walk that
  re-seeds every CanvasLayer's topmost Control (CanvasLayer
  interrupts the root-Window theme cascade in Godot 4).
- Late-mounting popups + modals get themed via a child_entered_tree
  hook on each CanvasLayer.

Build drawer thumbnails (scenes/ui/build_drawer_thumb.gd):
- New BuildDrawerThumb Control that dispatches on tool_id and draws
  a recognisable silhouette of the entity each tool builds. 17 tools
  covered: chop/mine/dig_grave/no_roof (Designate), stone+wood walls,
  wood+stone floors, door, crate, bed, torch, 5 workbenches
  (Carpenter/Smelter/Millstone/Hearth/Cremation Pyre), stockpile,
  graveyard.
- Replaces the flat ColorRect placeholder. _add_tool_btn signature
  changed from (label, color, callback) to (label, tool_id, callback).
This commit is contained in:
megaproxy 2026-05-16 16:09:56 +01:00
parent 413054157a
commit 53cb92041c
4 changed files with 614 additions and 30 deletions

View file

@ -20,6 +20,11 @@ const RESUME_TOAST_SCRIPT: Script = preload("res://scenes/ui/resume_toa
# Phase 17 — PawnDetailPanel (layer 18) and SettingsMenu (layer 26).
const PAWN_DETAIL_PANEL_SCRIPT: Script = preload("res://scenes/ui/pawn_detail_panel.gd")
const WORKBENCH_PANEL_SCRIPT: Script = preload("res://scenes/ui/workbench_panel.gd")
const MEDIEVAL_THEME_SCRIPT: Script = preload("res://scenes/ui/medieval_theme.gd")
# Built once in _ready and re-applied to any CanvasLayer-rooted Control because
# CanvasLayer doesn't propagate the root-Window theme cascade.
var _app_theme: Theme = null
const SETTINGS_MENU_SCRIPT: Script = preload("res://scenes/ui/settings_menu.gd")
# Phase 17 (Agent B) — BuildDrawer bottom-sheet (layer 16).
const BUILD_DRAWER_SCRIPT: Script = preload("res://scenes/ui/build_drawer.gd")
@ -40,6 +45,13 @@ func _ready() -> void:
assert(SaveSystem != null, "SaveSystem autoload missing")
assert(Autosave != null, "Autosave autoload missing")
# Medieval-warm Theme — assigned on the root Window first. Cascade alone
# doesn't reach Controls inside CanvasLayers (CanvasLayer has no theme
# property), so we also walk the tree post-mount and apply to every Control
# encountered. (2026-05-16 polish pass.)
_app_theme = MEDIEVAL_THEME_SCRIPT.build()
get_tree().root.theme = _app_theme
# Phase 15 — Storyteller UI layers. Runtime-instantiated so no .tscn edit is
# needed. CanvasLayer ensures correct draw order above World/TopBar regardless
# of parent-node position.
@ -156,3 +168,33 @@ func _ready() -> void:
top_bar._add_work_log_btns()
Audit.log("main", "Phase 17 (Agent C) — WorkPriorityMatrix + AlertsLog mounted.")
# Apply the medieval theme to every Control under each CanvasLayer.
# CanvasLayers interrupt the root-Window theme cascade so we have to seed
# each one explicitly. Defer one frame so panels that build their UI in
# _ready (PawnDetailPanel, WorkbenchPanel, BuildDrawer) finish first.
call_deferred("_apply_theme_to_canvas_layers")
## Walks the scene tree and assigns _app_theme to every Control directly under
## a CanvasLayer (the topmost Control in each layer's branch). From there the
## standard Control-to-Control cascade carries the theme to all descendants.
## Also catches popups and modals that mount later via child_entered_tree.
func _apply_theme_to_canvas_layers() -> void:
for c in get_children():
if c is CanvasLayer:
_seed_layer_theme(c)
# Watch for late additions (popup menus, modals).
if not c.child_entered_tree.is_connected(_on_layer_child_added):
c.child_entered_tree.connect(_on_layer_child_added)
func _seed_layer_theme(layer: CanvasLayer) -> void:
for c in layer.get_children():
if c is Control and c.theme == null:
c.theme = _app_theme
func _on_layer_child_added(node: Node) -> void:
if node is Control and node.theme == null:
node.theme = _app_theme