extends Object class_name UITheme ## Shared comic/cel UI theme: Bangers display font, paper panels with thick ## ink borders and hard drop shadows, papaya accent. Built once, applied to ## the root Window so every Control in the game inherits it. const FONT_PATH := "res://assets/ui/fonts/Bangers-Regular.ttf" const INK := Color(0.09, 0.08, 0.12) const PAPER := Color(0.98, 0.96, 0.9) const PAPER_DIM := Color(0.92, 0.9, 0.84) const PAPAYA := Color(1.0, 0.55, 0.15) const PAPAYA_HOT := Color(1.0, 0.68, 0.25) const TEAL := Color(0.16, 0.72, 0.68) static var _theme: Theme = null ## Comic panel stylebox: flat fill, thick ink border, hard offset shadow. static func box(bg: Color, border: Color = INK, radius: int = 10, border_w: int = 3, shadow: bool = true) -> StyleBoxFlat: var sb := StyleBoxFlat.new() sb.bg_color = bg sb.border_color = border sb.set_border_width_all(border_w) sb.set_corner_radius_all(radius) sb.content_margin_left = 16 sb.content_margin_right = 16 sb.content_margin_top = 8 sb.content_margin_bottom = 8 if shadow: sb.shadow_color = Color(INK.r, INK.g, INK.b, 0.55) sb.shadow_size = 0 sb.shadow_offset = Vector2(4, 4) sb.shadow_size = 2 return sb static func build() -> Theme: if _theme: return _theme var t := Theme.new() var font: Font = load(FONT_PATH) if ResourceLoader.exists(FONT_PATH) else null if font: t.default_font = font t.default_font_size = 26 # ── Buttons: paper card, papaya on hover, squash on press ──────────── t.set_stylebox("normal", "Button", box(PAPER)) t.set_stylebox("hover", "Button", box(PAPAYA_HOT)) t.set_stylebox("pressed", "Button", box(PAPAYA, INK, 10, 3, false)) t.set_stylebox("focus", "Button", box(PAPER, TEAL)) t.set_stylebox("disabled", "Button", box(PAPER_DIM, Color(0.4, 0.4, 0.45))) t.set_color("font_color", "Button", INK) t.set_color("font_hover_color", "Button", INK) t.set_color("font_pressed_color", "Button", PAPER) t.set_color("font_focus_color", "Button", INK) t.set_color("font_disabled_color", "Button", Color(0.45, 0.45, 0.5)) # OptionButton / CheckBox get the same treatment explicitly (Godot theme # types don't inherit styling from Button) for cls in ["OptionButton", "MenuButton", "CheckBox", "CheckButton"]: t.set_stylebox("normal", cls, box(PAPER)) t.set_stylebox("hover", cls, box(PAPAYA_HOT)) t.set_stylebox("pressed", cls, box(PAPAYA, INK, 10, 3, false)) t.set_stylebox("focus", cls, box(PAPER, TEAL)) t.set_stylebox("disabled", cls, box(PAPER_DIM, Color(0.4, 0.4, 0.45))) t.set_color("font_color", cls, INK) t.set_color("font_hover_color", cls, INK) t.set_color("font_pressed_color", cls, PAPER) t.set_color("font_focus_color", cls, INK) t.set_color("font_disabled_color", cls, Color(0.45, 0.45, 0.5)) # ── Labels: ink outline everywhere for that inked-cel readability ──── t.set_color("font_outline_color", "Label", INK) t.set_constant("outline_size", "Label", 8) t.set_color("font_color", "Label", PAPER) # RichTextLabel (killfeed) t.set_color("default_color", "RichTextLabel", PAPER) t.set_color("font_outline_color", "RichTextLabel", INK) t.set_constant("outline_size", "RichTextLabel", 6) # ── Inputs ─────────────────────────────────────────────────────────── t.set_stylebox("normal", "LineEdit", box(PAPER, INK, 8, 3, false)) t.set_stylebox("focus", "LineEdit", box(PAPER, TEAL, 8, 3, false)) t.set_color("font_color", "LineEdit", INK) t.set_color("caret_color", "LineEdit", INK) # ── Panels / lists ─────────────────────────────────────────────────── t.set_stylebox("panel", "PanelContainer", box(Color(0.13, 0.12, 0.18, 0.92), INK, 14, 4)) t.set_stylebox("panel", "ItemList", box(Color(0.13, 0.12, 0.18, 0.92), INK, 10, 3, false)) t.set_color("font_color", "ItemList", PAPER) # ── Popup menus (OptionButton dropdowns) ───────────────────────────── t.set_stylebox("panel", "PopupMenu", box(PAPER, INK, 8, 3, false)) t.set_color("font_color", "PopupMenu", INK) t.set_color("font_hover_color", "PopupMenu", PAPAYA) _theme = t return t ## Apply the theme to the whole root window (inherited by every Control in ## every scene from then on). Idempotent. static func apply_global(tree: SceneTree) -> void: if tree and tree.root and tree.root.theme != build(): tree.root.theme = build() ## Attach hover/click UI sounds to every BaseButton under `root` (recursive). static func wire_sounds(root: Node) -> void: var am = root.get_tree().root.get_node_or_null("AudioManager") if am == null: return for btn in root.find_children("*", "BaseButton", true, false): if btn.has_meta("ui_sfx_wired"): continue btn.set_meta("ui_sfx_wired", true) btn.mouse_entered.connect(func(): am.play_ui("ui_hover")) btn.pressed.connect(func(): am.play_ui("ui_click"))