diff --git a/README.md b/README.md index 138b1c9..82a9c4b 100644 --- a/README.md +++ b/README.md @@ -189,11 +189,13 @@ type ThemeManager = Manager[Theme] | Method | Behaviour | |------------------------|------------------------------------------------------------| | `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. | | `MustGet(name) T` | Same as `Get`, but panics on missing names. | | `Has(name) bool` | True if the name is registered. | | `Len() int` | Number of registered entries. | | `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. | | `Each(fn func(T))` | Iterates in insertion order. | | `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 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 diff --git a/core.manager.go b/core.manager.go index 895b3e4..c1e164d 100644 --- a/core.manager.go +++ b/core.manager.go @@ -33,6 +33,18 @@ func (m *Manager[T]) Register(v T) { 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) { v, ok := m.items[name] return v, ok @@ -59,6 +71,15 @@ func (m *Manager[T]) Names() []string { 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 { names := append([]string(nil), m.order...) sort.Strings(names) diff --git a/ui.manager.go b/ui.manager.go index cda87e7..65f9889 100644 --- a/ui.manager.go +++ b/ui.manager.go @@ -7,20 +7,13 @@ type UIManager = Manager[Widget] // order — used by the engine for top-down input dispatch (the widget // drawn on top gets the click first). func reversedWidgets(m *UIManager) []Widget { - names := m.Names() - out := make([]Widget, len(names)) - for i, n := range names { - out[len(names)-1-i] = m.MustGet(n) + all := m.All() + out := make([]Widget, len(all)) + for i, w := range all { + out[len(all)-1-i] = w } return out } // orderedWidgets iterates in registration order — bottom-up draw. -func orderedWidgets(m *UIManager) []Widget { - names := m.Names() - out := make([]Widget, len(names)) - for i, n := range names { - out[i] = m.MustGet(n) - } - return out -} +func orderedWidgets(m *UIManager) []Widget { return m.All() }