Settings Screen

a centred options panel with a live interface-size slider.

▶ Run in browser

Tags: ui settings game-ui

A settings screen is a column of labelled controls in a panel. FormLayout lines the labels up against their controls: form.add_field("Volume:", slider) adds the pair and returns the control, so it can be kept and wired in one line. The controls here are the common ones: a Slider (volume, 0 to 100), a CheckBox (fullscreen) and a DropDown (a quality preset). Each reports a change through its own signal (value_changed, toggled, item_selected), and the status line under the form re-reads every control whenever one fires. Apply echoes that state into the status line and Back reports that nothing was saved; nothing is stored.

The “UI scale” slider writes self.tree.ui_scale, the player’s interface size. Every Control in the tree follows it on the next frame, the slider under the pointer included, so dragging it from 1x to 2x grows the whole screen. Text is laid out at the size it is drawn, so it stays sharp at in-between scales.

A bigger interface needs somewhere to go. The panel is anchored with anchor=Anchor.CENTER and keeps its authored 500 x 340 where it fits; where it does not, it shrinks to the window less an 8-unit gutter. It measures the room with viewport_size, which a Control reads in UI units (the window divided by ui_scale), so at 2x an 800 x 600 window is 400 x 300 and the panel narrows to match. Its content sits in a ScrollContainer, so every control is still reachable on a small window at a large scale.

Source

 1# /// simvx
 2# tags = ["ui", "settings", "game-ui"]
 3# web = { width = 800, height = 600, responsive = true }
 4# ///
 5"""Settings Screen: a centred options panel with a live interface-size slider.
 6
 7A settings screen is a column of labelled controls in a panel. `FormLayout`
 8lines the labels up against their controls: `form.add_field("Volume:", slider)`
 9adds the pair and returns the control, so it can be kept and wired in one line.
10The controls here are the common ones: a `Slider` (volume, 0 to 100), a
11`CheckBox` (fullscreen) and a `DropDown` (a quality preset). Each reports a
12change through its own signal (`value_changed`, `toggled`, `item_selected`),
13and the status line under the form re-reads every control whenever one fires.
14Apply echoes that state into the status line and Back reports that nothing was
15saved; nothing is stored.
16
17The "UI scale" slider writes `self.tree.ui_scale`, the player's interface size.
18Every `Control` in the tree follows it on the next frame, the slider under the
19pointer included, so dragging it from 1x to 2x grows the whole screen. Text is
20laid out at the size it is drawn, so it stays sharp at in-between scales.
21
22A bigger interface needs somewhere to go. The panel is anchored with
23`anchor=Anchor.CENTER` and keeps its authored 500 x 340 where it fits; where it
24does not, it shrinks to the window less an 8-unit gutter. It measures the room
25with `viewport_size`, which a `Control` reads in UI units (the window divided
26by `ui_scale`), so at 2x an 800 x 600 window is 400 x 300 and the panel
27narrows to match. Its content sits in a `ScrollContainer`, so every control is
28still reachable on a small window at a large scale.
29"""
30
31from simvx.core import Anchor, Colour, Label, Node, Panel
32from simvx.core.ui import Button, CheckBox, DropDown, FormLayout, HBoxContainer, ScrollContainer, Slider, VBoxContainer
33from simvx.graphics import App
34
35QUALITY_PRESETS = ["Low", "Medium", "High", "Ultra"]
36PANEL_SIZE = (500.0, 340.0)  # the authored size, in UI units
37GUTTER = 8.0  # the least room the panel leaves at each window edge
38
39
40class SettingsDemo(Node):
41    def on_ready(self):
42        self.panel = self.add_child(Panel(name="SettingsPanel", anchor=Anchor.CENTER, size=PANEL_SIZE))
43        self.panel.bg_colour = Colour.hex("#1A1A2E")
44        scroll = self.panel.add_child(ScrollContainer(anchor=Anchor.FULL_RECT, inset=(24, 16, 16, 12)))
45        scroll.bg_colour = self.panel.bg_colour
46        column = scroll.add_child(VBoxContainer(separation=14))
47        column.add_child(Label("Settings", font_size=22, alignment="centre"))
48
49        form = column.add_child(FormLayout(separation=12))
50        self.scale_slider = form.add_field("UI scale:", Slider(1.0, 2.0, self.tree.ui_scale, size=(240, 24)))
51        self.scale_slider.step = 0.05
52        self.volume = form.add_field("Volume:", Slider(0, 100, 70, size=(240, 24)))
53        self.fullscreen = form.add_field("Fullscreen:", CheckBox("Enabled", checked=True))
54        self.quality = form.add_field("Quality:", DropDown(items=QUALITY_PRESETS, selected_index=2))
55
56        self.scale_slider.value_changed.connect(self.set_ui_scale)
57        self.volume.value_changed.connect(self.show_values)
58        self.fullscreen.toggled.connect(self.show_values)
59        self.quality.item_selected.connect(self.show_values)
60
61        self.status = column.add_child(Label(font_size=13))
62        self.status.text_colour = Colour.LIGHT_GRAY
63        buttons = column.add_child(HBoxContainer(separation=12))
64        buttons.add_child(Button("Apply", on_press=self.apply, size=(120, 34)))
65        buttons.add_child(Button("Back", on_press=self.back, size=(120, 34)))
66        self.show_values()
67
68    def on_update(self, dt):
69        # The room changes with the window and with ui_scale; follow both.
70        width, height = self.panel.viewport_size
71        self.panel.size = (min(PANEL_SIZE[0], width - 2 * GUTTER), min(PANEL_SIZE[1], height - 2 * GUTTER))
72
73    def set_ui_scale(self, value):
74        self.tree.ui_scale = value
75        self.show_values()
76
77    def summary(self) -> str:
78        fullscreen = "on" if self.fullscreen.checked else "off"
79        return (
80            f"UI {self.tree.ui_scale:.2f}x  |  Volume {self.volume.value:.0f}  |  "
81            f"Fullscreen {fullscreen}  |  Quality {self.quality.selected_text}"
82        )
83
84    def show_values(self, *_):
85        self.status.text = self.summary()
86
87    def apply(self):
88        self.status.text = f"Applied: {self.summary()}"
89
90    def back(self):
91        self.status.text = "Back pressed (no changes saved)"
92
93
94if __name__ == "__main__":
95    App(title="SimVX Settings", width=800, height=600).run(SettingsDemo())