widget manager refact

This commit is contained in:
2026-08-30 22:27:51 +02:00
parent 258cbf85c0
commit 3890edc946
26 changed files with 413 additions and 242 deletions
+46 -6
View File
@@ -49,7 +49,7 @@ All game code lives in one flat package, `inc`. There are no subdirectories: a
file's name carries the structure, in the form `[category].[name].go`.
- The category is always **singular**: `item`, `scene`, `background`,
`character`, `dialog`, `script`, `world`, `ui`, `theme`, `names`.
`character`, `dialog`, `script`, `world`, `widget`, `ui`, `theme`, `names`.
- `[category].manager.go` is the file that ties a category together — its
entity type and whatever else the category shares.
- Every other file in a category holds exactly one entity: one scene, one
@@ -65,9 +65,10 @@ inc/theme.manager.go the Theme alias and the colour helpers
inc/theme.realworld.go realworld-93
inc/theme.nokia_punk.go nokia-punk
inc/world.manager.go unsaved runtime state
inc/world.action.go custom actions and the action pump
inc/ui.manager.go HUD layout and widget registration
inc/ui.*.go custom widgets, coloured text
inc/world.action.go custom actions
inc/widget.manager.go the Widget alias, HUD layout, the visibility gate
inc/widget.*.go one widget per file, registering itself
inc/ui.text.go coloured text on a scratch image
inc/background.*.go one image asset per scene
inc/character.*.go the cast, tapes included
inc/item.*.go inventory
@@ -84,7 +85,7 @@ Every category that owns a collection of entities has a manager, and they are
all the engine's own `inkwell.Manager[T]` — the same registry type the `*Game`
hangs its content off. The game defines no registry of its own.
All seven are declared together, in `boot.go`, so the list of what the game
All eight are declared together, in `boot.go`, so the list of what the game
holds is one block rather than a line hidden in each category file:
```go
@@ -96,6 +97,7 @@ var (
SceneManager = inkwell.NewManager[Scene]()
ScriptManager = inkwell.NewManager[Script]()
ThemeManager = inkwell.NewManager[Theme]()
WidgetManager = inkwell.NewManager[Widget]()
)
```
@@ -146,6 +148,43 @@ into one.
Nothing may depend on it — including the order the arrow keys walk the scenes,
which is simply the order the files sit in.
### Widgets name their layer instead of their order
A widget is a file like any other entity — `widget.<name>.go`, one widget,
registering itself in an `init()` — and that includes the engine's own widgets,
which the game registers as literals the same way it registers a background:
```go
func init() {
WidgetManager.Register(&inkwell.Cursor{
Name: "cursor",
})
}
```
What a widget cannot take from alphabetical registration is its place in the
stack: the frame has to be drawn under the slots that sit on it, the cursor over
everything, and the use-with guard has to see a click after the HUD has had it.
So a widget declares a layer rather than inheriting one from the order it was
registered in — `inkwell.LayerScene`, `LayerPanel`, `LayerHUD`, `LayerSpeech`,
`LayerDialog`, `LayerMenu`, `LayerCurtain`, `LayerCursor`. The engine draws from
the bottom layer up and ticks from the top layer down, and registration order
only decides ties inside one layer:
```go
func (h *hudFrame) Layer() inkwell.Layer { return inkwell.LayerPanel }
```
A widget that says nothing sits on `LayerHUD`; ours all say it, because the
layer is the one thing about a widget the file cannot show. `gate`, the wrapper
that hides a widget outside `ModePlay`, returns `inkwell.LayerOf` of the widget
it wraps, so wrapping never moves anything.
Two widgets are worth knowing about because they are not decoration: `pump`
drives `World.PumpTick`, which is what runs every queued action, and
`usewith_guard` sits on `LayerScene` so that it ticks last and can answer a
use-with pair nobody authored.
### Handing a category to the engine
Nothing is copied. `inkwell.NewGame` builds its own empty managers, and `New`
@@ -159,6 +198,7 @@ g.DialogueManager = DialogManager
g.ItemManager = ItemManager
g.SceneManager = SceneManager
g.ScriptManager = ScriptManager
g.WidgetManager = WidgetManager
ThemeManager.Each(g.ThemeManager.Register)
```
@@ -221,7 +261,7 @@ constants in `names.manager.go`, the colour tokens, and the action constructors
(`TapeSay`, `Paused`, `SetMode`, `EnterScene`, `Back`, `Fn`).
Everything else is machinery and stays unexported: the HUD widgets
(`tapeSlots`, `letterbox`, `hudFrame`, …), `registerUI`, the runners,
(`tapeSlots`, `letterbox`, `hudFrame`, …), `gated`, the runners,
`prepareScene`, `sceneDefaults`, `fillSelectorPins`.
The word is **scene**, the engine's own. The wiki and the concept-art deck count