feat(theme): showcase harness — the human-eyeball proof
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
client/scenes/theme/theme_showcase.tscn
Normal file
5
client/scenes/theme/theme_showcase.tscn
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
[ext_resource type="Script" path="res://scripts/theme/theme_showcase.gd" id="1"]
|
||||||
|
[node name="ThemeShowcase" type="Control"]
|
||||||
|
anchors_preset = 15
|
||||||
|
script = ExtResource("1")
|
||||||
97
client/scripts/theme/theme_showcase.gd
Normal file
97
client/scripts/theme/theme_showcase.gd
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# client/scripts/theme/theme_showcase.gd
|
||||||
|
extends Control
|
||||||
|
## The eyeball proof for the shared Theme (spec §7). Builds every token, stylebox,
|
||||||
|
## font, surface, and bar on one scrollable screen. No AI, no state. §2: presentation.
|
||||||
|
const Palette = preload("res://scripts/theme/palette.gd")
|
||||||
|
const ThemeKeys = preload("res://scripts/theme/theme_keys.gd")
|
||||||
|
const GAME_THEME := "res://assets/theme/game_theme.tres"
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
theme = load(GAME_THEME)
|
||||||
|
set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
var scroll := ScrollContainer.new()
|
||||||
|
scroll.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
add_child(scroll)
|
||||||
|
var col := VBoxContainer.new()
|
||||||
|
col.custom_minimum_size = Vector2(1900, 0)
|
||||||
|
col.add_theme_constant_override("separation", 16)
|
||||||
|
scroll.add_child(col)
|
||||||
|
|
||||||
|
col.add_child(_heading("SHARED THEME — SHOWCASE"))
|
||||||
|
col.add_child(_swatches())
|
||||||
|
col.add_child(_variations())
|
||||||
|
col.add_child(_rarity())
|
||||||
|
col.add_child(_bars())
|
||||||
|
col.add_child(_surfaces())
|
||||||
|
|
||||||
|
|
||||||
|
func _heading(text: String) -> Label:
|
||||||
|
var l := Label.new()
|
||||||
|
l.text = text
|
||||||
|
l.theme_type_variation = &"Heading"
|
||||||
|
return l
|
||||||
|
|
||||||
|
|
||||||
|
func _swatches() -> Control:
|
||||||
|
var grid := GridContainer.new()
|
||||||
|
grid.columns = 8
|
||||||
|
for c in [Palette.STAGE_1, Palette.BAY_STRIPE_A, Palette.DARK_PANEL_70,
|
||||||
|
Palette.SHEET_TOP, Palette.PARCHMENT_CARD, Palette.BLOOD, Palette.GOLD,
|
||||||
|
Palette.STEEL, Palette.GREEN, Palette.INK_BODY, Palette.CREAM]:
|
||||||
|
var r := ColorRect.new()
|
||||||
|
r.color = c
|
||||||
|
r.custom_minimum_size = Vector2(120, 60)
|
||||||
|
grid.add_child(r)
|
||||||
|
return grid
|
||||||
|
|
||||||
|
|
||||||
|
func _variations() -> Control:
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
row.add_theme_constant_override("separation", 12)
|
||||||
|
var cta := Button.new(); cta.text = "ENTER THE WORLD"; cta.theme_type_variation = ThemeKeys.PRIMARY_CTA
|
||||||
|
var tab := Button.new(); tab.text = "INACTIVE"; tab.theme_type_variation = ThemeKeys.TAB
|
||||||
|
var tab_a := Button.new(); tab_a.text = "ACTIVE"; tab_a.theme_type_variation = ThemeKeys.TAB_ACTIVE
|
||||||
|
var chip := Button.new(); chip.text = "INTIMIDATE · STR"; chip.theme_type_variation = ThemeKeys.CHIP
|
||||||
|
for b in [cta, tab, tab_a, chip]:
|
||||||
|
row.add_child(b)
|
||||||
|
var card := PanelContainer.new(); card.theme_type_variation = ThemeKeys.PARCHMENT_CARD
|
||||||
|
card.custom_minimum_size = Vector2(160, 80)
|
||||||
|
row.add_child(card)
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
func _rarity() -> Control:
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
for i in range(5):
|
||||||
|
var tile := PanelContainer.new()
|
||||||
|
tile.theme_type_variation = ThemeKeys.ITEM_TILE
|
||||||
|
tile.custom_minimum_size = Vector2(72, 72)
|
||||||
|
var bar := ColorRect.new()
|
||||||
|
bar.color = Palette.rarity_color(i)
|
||||||
|
bar.custom_minimum_size = Vector2(60, 4)
|
||||||
|
tile.add_child(bar)
|
||||||
|
row.add_child(tile)
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
func _bars() -> Control:
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
for kind in [&"hp", &"mp", &"stamina"]:
|
||||||
|
var pair = Palette.stat_bar(kind)
|
||||||
|
var bar := ColorRect.new()
|
||||||
|
bar.color = pair[0]
|
||||||
|
bar.custom_minimum_size = Vector2(200, 18)
|
||||||
|
row.add_child(bar)
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
func _surfaces() -> Control:
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
row.custom_minimum_size = Vector2(0, 240)
|
||||||
|
for path in ["res://scenes/theme/surfaces/ParchmentPanel.tscn",
|
||||||
|
"res://scenes/theme/surfaces/DarkBay.tscn"]:
|
||||||
|
var s = load(path).instantiate()
|
||||||
|
s.custom_minimum_size = Vector2(600, 220)
|
||||||
|
row.add_child(s)
|
||||||
|
return row
|
||||||
1
client/scripts/theme/theme_showcase.gd.uid
Normal file
1
client/scripts/theme/theme_showcase.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ciqf5caopnsh3
|
||||||
11
client/tests/unit/test_theme_showcase.gd
Normal file
11
client/tests/unit/test_theme_showcase.gd
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# client/tests/unit/test_theme_showcase.gd
|
||||||
|
extends "res://addons/gut/test.gd"
|
||||||
|
|
||||||
|
|
||||||
|
func test_showcase_instances_without_error():
|
||||||
|
var packed = load("res://scenes/theme/theme_showcase.tscn")
|
||||||
|
assert_true(packed is PackedScene)
|
||||||
|
var node = packed.instantiate()
|
||||||
|
add_child_autofree(node) # runs _ready(); any engine error here fails the suite
|
||||||
|
assert_true(node is Control)
|
||||||
|
assert_true(node.theme is Theme, "showcase did not apply game_theme")
|
||||||
1
client/tests/unit/test_theme_showcase.gd.uid
Normal file
1
client/tests/unit/test_theme_showcase.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://e4p6b65e14mw
|
||||||
Reference in New Issue
Block a user