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 browser

Tags: 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    Slider,
 24    TabContainer,
 25    TextEdit,
 26    VBoxContainer,
 27)
 28from simvx.graphics import App
 29
 30# All theme presets to show
 31THEMES: list[tuple[str, AppTheme]] = [
 32    ("Dark", AppTheme.dark()),
 33    ("Abyss", AppTheme.abyss()),
 34    ("Midnight", AppTheme.midnight()),
 35    ("Light", AppTheme.light()),
 36    ("Monokai", AppTheme.monokai()),
 37    ("Solarised", AppTheme.solarised_dark()),
 38    ("Nord", AppTheme.nord()),
 39]
 40
 41COL_W = 210.0
 42PAD = 8.0
 43
 44
 45class ThemeColumn(VBoxContainer):
 46    """A column showing widgets rendered with a specific theme."""
 47
 48    def __init__(self, theme_name: str, theme: AppTheme, **kwargs):
 49        super().__init__(**kwargs)
 50        self.separation = 4.0
 51        self._app_theme = theme
 52        # Assign theme to this subtree so all children inherit it
 53        self.theme = theme
 54
 55        # --- Column background panel (uses theme bg) ---
 56        # (We are the VBox, our draw fills the column bg)
 57
 58        # Title
 59        title = Label(f" {theme_name} ")
 60        title.font_size = 15.0
 61        title.alignment = "center"
 62        title.size_x = COL_W
 63        self.add_child(title)
 64
 65        # --- Buttons ---
 66        self._section("Buttons")
 67        for label, state in [
 68            ("Normal", Button.VisualState.NORMAL),
 69            ("Hover", Button.VisualState.HOVER),
 70            ("Pressed", Button.VisualState.PRESSED),
 71            ("Disabled", Button.VisualState.DISABLED),
 72            ("Focused", Button.VisualState.FOCUSED),
 73        ]:
 74            btn = Button(label)
 75            btn.size_x = COL_W - 2 * PAD
 76            btn.size_y = 26
 77            btn.set_visual_state_override(state)
 78            self.add_child(btn)
 79
 80        # --- Panel ---
 81        self._section("Panel")
 82        panel = Panel()
 83        panel.size_x = COL_W - 2 * PAD
 84        panel.size_y = 36
 85        pl = Label("Panel content")
 86        pl.font_size = 12.0
 87        pl.set_anchor_preset(AnchorPreset.TOP_LEFT)
 88        pl.margin_left = 6
 89        pl.margin_top = 8
 90        panel.add_child(pl)
 91        self.add_child(panel)
 92
 93        # --- TextEdit ---
 94        self._section("TextEdit")
 95        edit = TextEdit("Editable text")
 96        edit.size_x = COL_W - 2 * PAD
 97        edit.size_y = 26
 98        self.add_child(edit)
 99
100        edit_f = TextEdit("Focused input")
101        edit_f.size_x = COL_W - 2 * PAD
102        edit_f.size_y = 26
103        edit_f.focused = True
104        self.add_child(edit_f)
105
106        # --- Slider ---
107        self._section("Slider")
108        sl = Slider(0, 100, value=65)
109        sl.size_x = COL_W - 2 * PAD
110        sl.size_y = 18
111        self.add_child(sl)
112
113        # --- ProgressBar ---
114        self._section("Progress")
115        pb = ProgressBar(0, 100)
116        pb.value = 72
117        pb.size_x = COL_W - 2 * PAD
118        pb.size_y = 18
119        self.add_child(pb)
120
121        # --- CheckBox ---
122        self._section("CheckBox")
123        cb_on = CheckBox("Enabled", checked=True)
124        self.add_child(cb_on)
125        cb_off = CheckBox("Disabled feature")
126        self.add_child(cb_off)
127
128        # --- Tabs ---
129        self._section("Tabs")
130        tabs = TabContainer()
131        tabs.size_x = COL_W - 2 * PAD
132        tabs.size_y = 60
133        for name in ("Scene", "Debug", "Output"):
134            tabs.add_child(Control(name=name))
135        self.add_child(tabs)
136
137    def _section(self, name: str):
138        lbl = Label(f"  {name}")
139        lbl.font_size = 10.0
140        self.add_child(lbl)
141
142    def on_draw(self, renderer):
143        """Draw themed column background before children."""
144        x, y, w, h = self.get_global_rect()
145        t = self._app_theme
146        renderer.draw_rect((x, y), (w, h), colour=t.bg, filled=True)
147        # Subtle top accent bar
148        renderer.draw_rect((x, y), (w, 2), colour=t.accent, filled=True)
149
150
151class ThemeDemoRoot(Control):
152    """Root: horizontal row of themed columns with scroll support."""
153
154    def __init__(self):
155        super().__init__()
156        self._hbox = HBoxContainer()
157        self._hbox.separation = 4.0
158        self.add_child(self._hbox)
159
160        for name, theme in THEMES:
161            col = ThemeColumn(name, theme)
162            col.size_x = COL_W
163            col.size_y = 780
164            self._hbox.add_child(col)
165
166    def on_ready(self):
167        self.set_anchor_preset(AnchorPreset.FULL_RECT)
168        self.margin_left = 6
169        self.margin_top = 6
170        self._hbox.size_x = len(THEMES) * (COL_W + 4)
171        self._hbox.size_y = 780
172
173if __name__ == "__main__":
174    total_w = int(len(THEMES) * (COL_W + 4) + 16)
175    # bg_colour omitted → clear colour auto-syncs from the active theme's bg_black
176    app = App(width=total_w, height=800, title="StyleBox Theme Demo")
177    app.run(ThemeDemoRoot())