Visual demo for the StyleBox theming system.¶
Shows all theme presets side by side. Each column applies its own theme to the widget subtree so backgrounds, buttons, inputs, sliders, checkboxes, and tabs all render with the correct colours. Buttons are shown in every state: normal, hover, pressed, disabled, focused.
▶ Run in browserTags: ui
Run: uv run python examples/features/ui/theme.py
Source¶
1#!/usr/bin/env python3
2"""Visual demo for the StyleBox theming system.
3
4Shows all theme presets side by side. Each column applies its own theme to
5the widget subtree so backgrounds, buttons, inputs, sliders, checkboxes,
6and tabs all render with the correct colours. Buttons are shown in every
7state: normal, hover, pressed, disabled, focused.
8
9Run:
10 uv run python examples/features/ui/theme.py
11"""
12
13from simvx.core.ui import (
14 AnchorPreset,
15 AppTheme,
16 Button,
17 CheckBox,
18 Control,
19 HBoxContainer,
20 Label,
21 Panel,
22 ProgressBar,
23 ScrollContainer,
24 Slider,
25 TabContainer,
26 TextEdit,
27 VBoxContainer,
28)
29from simvx.graphics import App
30
31# All theme presets to show
32THEMES: list[tuple[str, AppTheme]] = [
33 ("Dark", AppTheme.dark()),
34 ("Abyss", AppTheme.abyss()),
35 ("Midnight", AppTheme.midnight()),
36 ("Light", AppTheme.light()),
37 ("Monokai", AppTheme.monokai()),
38 ("Solarised", AppTheme.solarised_dark()),
39 ("Nord", AppTheme.nord()),
40]
41
42COL_W = 210.0
43COL_H = 780.0
44PAD = 8.0
45
46
47class ThemeColumn(VBoxContainer):
48 """A column showing widgets rendered with a specific theme."""
49
50 def __init__(self, theme_name: str, theme: AppTheme, **kwargs):
51 super().__init__(**kwargs)
52 self.separation = 4.0
53 self._app_theme = theme
54 # Assign theme to this subtree so all children inherit it
55 self.theme = theme
56
57 # --- Column background panel (uses theme bg) ---
58 # (We are the VBox, our draw fills the column bg)
59
60 # Title
61 title = Label(f" {theme_name} ")
62 title.font_size = 15.0
63 title.alignment = "center"
64 title.size_x = COL_W
65 self.add_child(title)
66
67 # --- Buttons ---
68 self._section("Buttons")
69 for label, state in [
70 ("Normal", Button.VisualState.NORMAL),
71 ("Hover", Button.VisualState.HOVER),
72 ("Pressed", Button.VisualState.PRESSED),
73 ("Disabled", Button.VisualState.DISABLED),
74 ("Focused", Button.VisualState.FOCUSED),
75 ]:
76 btn = Button(label)
77 btn.size_x = COL_W - 2 * PAD
78 btn.size_y = 26
79 btn.set_visual_state_override(state)
80 self.add_child(btn)
81
82 # --- Panel ---
83 self._section("Panel")
84 panel = Panel()
85 panel.size_x = COL_W - 2 * PAD
86 panel.size_y = 36
87 pl = Label("Panel content")
88 pl.font_size = 12.0
89 pl.set_anchor_preset(AnchorPreset.TOP_LEFT)
90 pl.margin_left = 6
91 pl.margin_top = 8
92 panel.add_child(pl)
93 self.add_child(panel)
94
95 # --- TextEdit ---
96 self._section("TextEdit")
97 edit = TextEdit("Editable text")
98 edit.size_x = COL_W - 2 * PAD
99 edit.size_y = 26
100 self.add_child(edit)
101
102 edit_f = TextEdit("Focused input")
103 edit_f.size_x = COL_W - 2 * PAD
104 edit_f.size_y = 26
105 edit_f.focused = True
106 self.add_child(edit_f)
107
108 # --- Slider ---
109 self._section("Slider")
110 sl = Slider(0, 100, value=65)
111 sl.size_x = COL_W - 2 * PAD
112 sl.size_y = 18
113 self.add_child(sl)
114
115 # --- ProgressBar ---
116 self._section("Progress")
117 pb = ProgressBar(0, 100)
118 pb.value = 72
119 pb.size_x = COL_W - 2 * PAD
120 pb.size_y = 18
121 self.add_child(pb)
122
123 # --- CheckBox ---
124 self._section("CheckBox")
125 cb_on = CheckBox("Enabled", checked=True)
126 self.add_child(cb_on)
127 cb_off = CheckBox("Disabled feature")
128 self.add_child(cb_off)
129
130 # --- Tabs ---
131 self._section("Tabs")
132 tabs = TabContainer()
133 tabs.size_x = COL_W - 2 * PAD
134 tabs.size_y = 60
135 for name in ("Scene", "Debug", "Output"):
136 tabs.add_child(Control(name=name))
137 self.add_child(tabs)
138
139 def _section(self, name: str):
140 lbl = Label(f" {name}")
141 lbl.font_size = 10.0
142 self.add_child(lbl)
143
144 def on_draw(self, renderer):
145 """Draw themed column background before children."""
146 x, y, w, h = self.get_global_rect()
147 t = self._app_theme
148 renderer.draw_rect((x, y), (w, h), colour=t.bg, filled=True)
149 # Subtle top accent bar
150 renderer.draw_rect((x, y), (w, 2), colour=t.accent, filled=True)
151
152
153class ThemeDemoRoot(Control):
154 """Root: a scrollable horizontal row of themed columns.
155
156 The columns are wrapped in a ScrollContainer so every theme and the full
157 height of each column (down to the Tabs row) stay reachable at any window
158 size: the mouse wheel scrolls vertically and the arrow keys scroll
159 horizontally.
160 """
161
162 def __init__(self):
163 super().__init__()
164 self._scroll = ScrollContainer()
165 self.add_child(self._scroll)
166
167 self._hbox = HBoxContainer()
168 self._hbox.separation = 4.0
169 self._scroll.add_child(self._hbox)
170
171 for name, theme in THEMES:
172 col = ThemeColumn(name, theme)
173 col.size_x = COL_W
174 col.size_y = COL_H
175 self._hbox.add_child(col)
176
177 def on_ready(self):
178 self.set_anchor_preset(AnchorPreset.FULL_RECT)
179 self.margin_left = 6
180 self.margin_top = 6
181 self._scroll.set_anchor_preset(AnchorPreset.FULL_RECT)
182 self._hbox.size_x = len(THEMES) * (COL_W + 4)
183 self._hbox.size_y = COL_H
184
185if __name__ == "__main__":
186 total_w = int(len(THEMES) * (COL_W + 4) + 16)
187 # bg_colour omitted → clear colour auto-syncs from the active theme's bg_black
188 app = App(width=total_w, height=800, title="StyleBox Theme Demo")
189 app.run(ThemeDemoRoot())