nodes/title.pyΒΆ
Part of Procedural Planets.
1"""TitleOverlay: the title card the port opens on, before the planet is interactive.
2
3The overlay is a plain anchored `Control` tree: a full-rect dimming `Panel`
4holding a centred `VBoxContainer` with the title, a one-line blurb, the control
5list and a start button. Anchors (not absolute positions) keep it centred when
6the window resizes.
7"""
8
9from __future__ import annotations
10
11from collections.abc import Callable, Sequence
12
13from simvx.core import Vec2
14from simvx.core.ui import AnchorPreset, Button, Control, Label, Panel, SizeFlags, VBoxContainer
15
16BLOCK_WIDTH = 640.0
17
18
19def _line(text: str, font_size: float, colour: tuple[float, float, float, float]) -> Label:
20 """Build a full-width, centred, single-line label of the given size."""
21 label = Label(text=text)
22 label.font_size = font_size
23 label.alignment = "center"
24 label.text_colour = colour
25 label.size = Vec2(BLOCK_WIDTH, font_size * 1.6)
26 return label
27
28
29class TitleOverlay(Panel):
30 """Dimmed title card listing the controls, dismissed by the start button.
31
32 `on_start` fires when the button is pressed. The owner is expected to
33 remove the overlay from the tree at that point; the overlay keeps no state
34 of its own.
35 """
36
37 def __init__(self, on_start: Callable[[], None], controls: Sequence[str] = (), **kwargs):
38 super().__init__(**kwargs)
39 self.set_anchor_preset(AnchorPreset.FULL_RECT)
40 self.bg_colour = (0.02, 0.03, 0.07, 0.86)
41
42 rows: list[Control] = [
43 _line("Procedural Planets", 40.0, (0.96, 0.97, 1.0, 1.0)),
44 _line("A quad-sphere world rebuilt from noise, live.", 18.0, (0.72, 0.78, 0.92, 1.0)),
45 ]
46 rows.extend(_line(text, 16.0, (0.62, 0.68, 0.82, 1.0)) for text in controls)
47
48 self.start_button = Button(text="Explore the planet", on_press=on_start)
49 self.start_button.font_size = 18.0
50 self.start_button.size = Vec2(260.0, 46.0)
51 # Labels fill the block width so their centred text lines up; the
52 # button keeps its own width and centres itself instead.
53 self.start_button.size_flags_horizontal = SizeFlags.SHRINK_CENTER
54 rows.append(self.start_button)
55
56 separation = 8.0
57 height = sum(row.size.y for row in rows) + separation * (len(rows) - 1)
58
59 self._vbox = VBoxContainer()
60 self._vbox.separation = separation
61 self._vbox.alignment = "center"
62 self._vbox.size = Vec2(BLOCK_WIDTH, height)
63 # CENTER anchors all four corners at the parent's midpoint, so the
64 # margins below spell out a BLOCK_WIDTH x height box around it.
65 self._vbox.set_anchor_preset(AnchorPreset.CENTER)
66 self._vbox.margin_left = -BLOCK_WIDTH / 2
67 self._vbox.margin_right = BLOCK_WIDTH / 2
68 self._vbox.margin_top = -height / 2
69 self._vbox.margin_bottom = height / 2
70 self.add_child(self._vbox)
71
72 for row in rows:
73 self._vbox.add_child(row)