Manager.Set and Manager.All
ci/woodpecker/push/woodpecker Pipeline was successful

Set is the deliberate overwrite: it replaces a registered entry in place,
keeping its position in the insertion order, and falls back to Register
when the name is new. Register keeps panicking on duplicates.

All returns every entry in insertion order; orderedWidgets is now just
that call, and reversedWidgets builds on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 09:50:28 +02:00
co-authored by Claude Opus 5
parent f9745e4266
commit 08f15a7e3d
3 changed files with 33 additions and 12 deletions
+7
View File
@@ -189,11 +189,13 @@ type ThemeManager = Manager[Theme]
| Method | Behaviour | | Method | Behaviour |
|------------------------|------------------------------------------------------------| |------------------------|------------------------------------------------------------|
| `Register(v T)` | Adds `v` to the registry. Panics on empty or duplicate `Name`. | | `Register(v T)` | Adds `v` to the registry. Panics on empty or duplicate `Name`. |
| `Set(v T)` | Replaces an entry in place, keeping its position; registers it when the name is new. |
| `Get(name) (T, bool)` | Looks up by name. The boolean is `false` if absent. | | `Get(name) (T, bool)` | Looks up by name. The boolean is `false` if absent. |
| `MustGet(name) T` | Same as `Get`, but panics on missing names. | | `MustGet(name) T` | Same as `Get`, but panics on missing names. |
| `Has(name) bool` | True if the name is registered. | | `Has(name) bool` | True if the name is registered. |
| `Len() int` | Number of registered entries. | | `Len() int` | Number of registered entries. |
| `Names() []string` | Returns names in **insertion order** (used for widget Z-order). | | `Names() []string` | Returns names in **insertion order** (used for widget Z-order). |
| `All() []T` | Returns every entry in **insertion order**. |
| `SortedNames() []string` | Returns names alphabetically. | | `SortedNames() []string` | Returns names alphabetically. |
| `Each(fn func(T))` | Iterates in insertion order. | | `Each(fn func(T))` | Iterates in insertion order. |
| `Remove(name string)` | Drops a registration; silent no-op if unknown. | | `Remove(name string)` | Drops a registration; silent no-op if unknown. |
@@ -216,6 +218,11 @@ on startup. Crashing loudly during `Build()` surfaces the problem in
development; quietly accepting the second registration would silently mask development; quietly accepting the second registration would silently mask
shadowed entities at runtime. shadowed entities at runtime.
When a registration genuinely has to be rewritten — a derived field filled in
after the fact, a hot-reloaded entity — `Set` is the deliberate overwrite. It
keeps the entry's place in the insertion order, so widget Z-order and any other
order-sensitive iteration survive the rewrite.
--- ---
## 4. The Game aggregate ## 4. The Game aggregate
+21
View File
@@ -33,6 +33,18 @@ func (m *Manager[T]) Register(v T) {
m.order = append(m.order, name) m.order = append(m.order, name)
} }
// Set replaces a registered entry in place, keeping its position in the
// registration order, and registers the entry when the name is new. Register
// panics on a duplicate; Set is the deliberate overwrite.
func (m *Manager[T]) Set(v T) {
name := v.GetName()
if _, ok := m.items[name]; !ok {
m.Register(v)
return
}
m.items[name] = v
}
func (m *Manager[T]) Get(name string) (T, bool) { func (m *Manager[T]) Get(name string) (T, bool) {
v, ok := m.items[name] v, ok := m.items[name]
return v, ok return v, ok
@@ -59,6 +71,15 @@ func (m *Manager[T]) Names() []string {
return append([]string(nil), m.order...) return append([]string(nil), m.order...)
} }
// All returns every registered entry in insertion order.
func (m *Manager[T]) All() []T {
out := make([]T, len(m.order))
for i, n := range m.order {
out[i] = m.items[n]
}
return out
}
func (m *Manager[T]) SortedNames() []string { func (m *Manager[T]) SortedNames() []string {
names := append([]string(nil), m.order...) names := append([]string(nil), m.order...)
sort.Strings(names) sort.Strings(names)
+5 -12
View File
@@ -7,20 +7,13 @@ type UIManager = Manager[Widget]
// order — used by the engine for top-down input dispatch (the widget // order — used by the engine for top-down input dispatch (the widget
// drawn on top gets the click first). // drawn on top gets the click first).
func reversedWidgets(m *UIManager) []Widget { func reversedWidgets(m *UIManager) []Widget {
names := m.Names() all := m.All()
out := make([]Widget, len(names)) out := make([]Widget, len(all))
for i, n := range names { for i, w := range all {
out[len(names)-1-i] = m.MustGet(n) out[len(all)-1-i] = w
} }
return out return out
} }
// orderedWidgets iterates in registration order — bottom-up draw. // orderedWidgets iterates in registration order — bottom-up draw.
func orderedWidgets(m *UIManager) []Widget { func orderedWidgets(m *UIManager) []Widget { return m.All() }
names := m.Names()
out := make([]Widget, len(names))
for i, n := range names {
out[i] = m.MustGet(n)
}
return out
}