git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit774784f34f4839cdefd90c8521d5e5c2fe7604ef
parent46e6a58b8f
authorLucas Galante <[email protected]>
date2026-07-12 19:29
refactor(widget)!: base() is guaranteed — the flip part 1 (6bd)

`Element::base`/`base_mut` return `&Widget`/`&mut Widget` directly: the
Option escape hatch is gone, and with it the WidgetId(0) no-base sentinel
class (hit_test/cursor_moved/focus/coverage queries all carry real ids now).
`as_any`/`as_any_mut`/`as_ptr`/`as_ptr_mut` became required — their defaults
manufactured DummyAny/null-DummyElement stand-ins nothing could use;
`impl_widget_base!` provides all six for test shims (layout/arena/tree
mocks grew a base field).

Every Option-handling call site rewrote 1:1 (map/map_or/and_then/unwrap/
if-let over a base that always exists). Adapted was already the only
production implementor.

Verified: 163 tests; workspace builds; settings audio render stream
byte-identical; files + data-editor A/B AE=0; live settings page-dropdown
popover open -> Fonts page switch.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018u7qTwzX95dd5ysAkaSCLk

 src/context.rs                   |  41 ++++------
 src/layout.rs                    |  39 +++++----
 src/main.rs                      |   2 +-
 src/scene/arena.rs               |  10 ++-
 src/scene/painter.rs             |   3 +-
 src/scene/tree.rs                |   9 ++-
 src/widget/container/treelist.rs |  14 ++--
 src/widget/core.rs               |  32 +++-----
 src/widget/display/serialize.rs  |   6 +-
 src/widget/input/ramp.rs         |  16 ++--
 src/widget/layout_helper.rs      |   4 +-
 src/widget/mod.rs                | 170 +++++++++++++--------------------------
 src/widget/model.rs              |  53 +++++-------
 13 files changed, 160 insertions(+), 239 deletions(-)

diff --git a/src/context.rs b/src/context.rs
index 9346eda..f05994f 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -308,9 +308,7 @@ impl UiContext {
                         }
                         if self.propagate_event_impl(&local_adjusted, child) {
                             if check_drag_target {
-                                if let Some(b) = (*child).base() {
-                                    self.drag_target = Some(b.id());
-                                }
+                                self.drag_target = Some((*child).base().id());
                             }
                             return true;
                         }
@@ -318,9 +316,7 @@ impl UiContext {
                     if (*root).handle_event(event, self) {
                         (*root).mark_dirty(self);
                         if check_drag_target {
-                            if let Some(b) = (*root).base() {
-                                self.drag_target = Some(b.id());
-                            }
+                            self.drag_target = Some((*root).base().id());
                         }
                         return true;
                     }
@@ -340,9 +336,7 @@ impl UiContext {
             self.tree.iter_registered().map(|(_, ptr)| ptr).collect();
         for ptr in ptrs {
             unsafe {
-                if let Some(b) = (*ptr).base_mut() {
-                    b.dirty = false;
-                }
+                (*ptr).base_mut().dirty = false;
             }
         }
         self.rebuild_spatial_grid();
@@ -418,7 +412,7 @@ impl UiContext {
 
     // --- Focus management (id-keyed; Phase 6bc) ---
     pub fn set_focused(&mut self, w: &mut dyn Element) {
-        let Some(id) = w.base().map(|b| b.id()) else { return };
+        let id = w.base().id();
         // Refresh the registry with the pointer we were just handed, so focus on a
         // not-yet-registered widget keeps working (the legacy code stored this pointer
         // directly; the id must resolve for FocusOut/KeyInput dispatch to reach it).
@@ -436,7 +430,7 @@ impl UiContext {
         if new_ptr.is_null() {
             return;
         }
-        let Some(id) = (unsafe { (*new_ptr).base().map(|b| b.id()) }) else { return };
+        let id = unsafe { (*new_ptr).base().id() };
         self.tree.register(id, new_ptr);
         self.set_focused_id(id);
     }
@@ -468,10 +462,7 @@ impl UiContext {
     }
 
     pub fn is_focused(&self, w: &dyn Element) -> bool {
-        match w.base() {
-            Some(b) => self.is_focused_id(b.id()),
-            None => false,
-        }
+        self.is_focused_id(w.base().id())
     }
 
     pub fn is_focused_id(&self, id: WidgetId) -> bool {
@@ -490,10 +481,8 @@ impl UiContext {
     }
 
     pub fn clear_if_matches(&mut self, w: &dyn Element) {
-        if let Some(b) = w.base() {
-            if self.focused_widget == Some(b.id()) {
-                self.focused_widget = None;
-            }
+        if self.focused_widget == Some(w.base().id()) {
+            self.focused_widget = None;
         }
     }
 
@@ -600,7 +589,7 @@ impl UiContext {
     /// Register an open popover. Takes `&mut` so the registry can be refreshed with the
     /// pointer we are handed (the occlusion walks resolve the stored id through the tree).
     pub fn register_popover(&mut self, w: &mut (dyn Element + 'static)) {
-        let Some(id) = w.base().map(|b| b.id()) else { return };
+        let id = w.base().id();
         self.tree.register(id, w as *mut (dyn Element + 'static));
         if !self.active_popovers.contains(&id) {
             self.active_popovers.push(id);
@@ -611,7 +600,7 @@ impl UiContext {
         if ptr.is_null() {
             return;
         }
-        let Some(id) = (unsafe { (*ptr).base().map(|b| b.id()) }) else { return };
+        let id = unsafe { (*ptr).base().id() };
         self.tree.register(id, ptr);
         if !self.active_popovers.contains(&id) {
             self.active_popovers.push(id);
@@ -619,9 +608,8 @@ impl UiContext {
     }
 
     /// Whether `(px, py)` is covered by an open popover or a popover-carrying widget other
-    /// than `query_id` (the querying widget excludes itself). Pass `WidgetId(0)` for a widget
-    /// with no base — ids start at 1, so it matches nothing, like the legacy address of a
-    /// widget that could never be registered.
+    /// than `query_id` (the querying widget excludes itself). Every widget has a base id
+    /// now (the flip) — the old `WidgetId(0)` no-base sentinel is gone.
     pub fn is_coordinate_covered(&self, query_id: WidgetId, px: f32, py: f32) -> bool {
         for &pop_id in self.active_popovers.iter() {
             if pop_id == query_id {
@@ -708,7 +696,7 @@ impl UiContext {
         if target.is_null() {
             return;
         }
-        let Some(id) = (unsafe { (*target).base().map(|b| b.id()) }) else { return };
+        let id = unsafe { (*target).base().id() };
         self.tree.register(id, target);
         crate::widget::context_menu::show(x, y, options, header_count, id);
     }
@@ -737,7 +725,8 @@ impl UiContext {
         };
 
         let mut config_info = None;
-        if let Some(b) = unsafe { (*target).base() } {
+        {
+            let b = unsafe { (*target).base() };
             if let (Some(ref file), Some(ref key)) = (&b.config_file, &b.config_key) {
                 config_info = Some((file.clone(), key.clone()));
             }
diff --git a/src/layout.rs b/src/layout.rs
index 30be521..7a8d500 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -3142,7 +3142,7 @@ impl RenderTarget for PopoverCollector {
 
 
 pub fn render_widget<T: Element + 'static>(pc: &mut dyn RenderTarget, w: &mut T, x: f32, y: f32, ww: f32, wh: f32, ctx: &mut UiContext) {
-    let id = w.base().map(|b| b.id());
+    let id = Some(w.base().id());
     if let Some(w_id) = id {
         ctx.register_widget(w_id, w.as_ptr_mut());
     }
@@ -5096,6 +5096,7 @@ mod tests {
     }
 
     struct MockWidget {
+        base: crate::widget::Widget,
         x: f32,
         y: f32,
         w: f32,
@@ -5103,6 +5104,7 @@ mod tests {
     }
 
     impl Element for MockWidget {
+        crate::impl_widget_base!(MockWidget);
         fn rect(&self) -> (f32, f32, f32, f32) {
             (self.x, self.y, self.w, self.h)
         }
@@ -5122,12 +5124,7 @@ mod tests {
     }
 
     impl Element for MockWidgetWithLabel {
-        fn base(&self) -> Option<&crate::widget::Widget> {
-            Some(&self.base)
-        }
-        fn base_mut(&mut self) -> Option<&mut crate::widget::Widget> {
-            Some(&mut self.base)
-        }
+        crate::impl_widget_base!(MockWidgetWithLabel);
         fn rect(&self) -> (f32, f32, f32, f32) {
             let offset = crate::widget::label_offset(self);
             (self.base.x, self.base.y - offset, self.base.w, self.base.h + offset)
@@ -5161,14 +5158,14 @@ mod tests {
         let mut stack = sec.vstack(&mut mock_pc, 10.0);
 
         let mut dummy = crate::context::UiContext::new();
-        let mut w1 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w1 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         stack.add_widget(&mut w1, 50.0, 30.0, &mut dummy);
 
         // Standard margin should be applied
         assert_eq!(w1.x, 38.0);
         assert_eq!(w1.y, start_y);
 
-        let mut w2 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w2 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         stack.add_widget(&mut w2, 60.0, 40.0, &mut dummy);
 
         // Second widget should start after first widget height + vstack spacing
@@ -5227,7 +5224,7 @@ mod tests {
         assert!(subsec.is_child);
         
         let mut dummy = crate::context::UiContext::new();
-        let mut w = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         subsec.widget(&mut pc, &mut w, 12.0, 100.0, 40.0, &mut dummy);
         
         let bottom = subsec.finish(&mut pc);
@@ -5312,10 +5309,10 @@ mod tests {
         assert_eq!(ctx.cw, 500.0);
 
         let mut ui_ctx = crate::context::UiContext::new();
-        let mut w1 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w1 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         ctx.widget(&mut w1, 12.0, 100.0, 40.0, &mut ui_ctx);
 
-        let mut w2 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w2 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         ctx.widget(&mut w2, 12.0, 100.0, 30.0, &mut ui_ctx);
 
         // Since cw=500, we should have multiple columns!
@@ -5333,10 +5330,10 @@ mod tests {
         assert_eq!(ctx.grid.col_heights.len(), 1);
         
         let mut ui_ctx = crate::context::UiContext::new();
-        let mut w1 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w1 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         ctx.widget(&mut w1, 12.0, 100.0, 40.0, &mut ui_ctx);
 
-        let mut w2 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w2 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         ctx.widget(&mut w2, 12.0, 100.0, 30.0, &mut ui_ctx);
 
         // Since it's a child section, we should have a single column only, so w1.x == w2.x.
@@ -5371,7 +5368,7 @@ mod tests {
         assert_eq!(ctx.grid.col_heights.len(), 2);
 
         let mut ui_ctx = crate::context::UiContext::new();
-        let mut w1 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
+        let mut w1 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
         ctx.widget(&mut w1, 12.0, 100.0, 40.0, &mut ui_ctx); // placed in col 0
 
         let height_col_0_before = ctx.grid.col_heights[0];
@@ -5460,9 +5457,9 @@ mod tests {
         };
 
         let mut dummy = crate::context::UiContext::new();
-        let mut w1 = MockWidget { x: 0.0, y: 0.0, w: 100.0, h: 50.0 };
-        let mut w2 = MockWidget { x: 0.0, y: 0.0, w: 100.0, h: 80.0 };
-        let mut w3 = MockWidget { x: 0.0, y: 0.0, w: 80.0, h: 40.0 };
+        let mut w1 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 100.0, h: 50.0 };
+        let mut w2 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 100.0, h: 80.0 };
+        let mut w3 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 80.0, h: 40.0 };
         
         let children = vec![
             &mut w1 as *mut MockWidget as *mut (dyn Element + 'static),
@@ -5492,9 +5489,9 @@ mod tests {
         };
 
         let mut dummy = crate::context::UiContext::new();
-        let mut w1 = MockWidget { x: 0.0, y: 0.0, w: 100.0, h: 50.0 };
-        let mut w2 = MockWidget { x: 0.0, y: 0.0, w: 100.0, h: 80.0 };
-        let mut w3 = MockWidget { x: 0.0, y: 0.0, w: 80.0, h: 40.0 };
+        let mut w1 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 100.0, h: 50.0 };
+        let mut w2 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 100.0, h: 80.0 };
+        let mut w3 = MockWidget { base: crate::widget::Widget::new(), x: 0.0, y: 0.0, w: 80.0, h: 40.0 };
         
         let children = vec![
             &mut w1 as *mut MockWidget as *mut (dyn Element + 'static),
diff --git a/src/main.rs b/src/main.rs
index 97487c3..84bd5e4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -175,7 +175,7 @@ impl Application for DemoApp {
             let self_ptr = self as *mut Self;
             unsafe {
                 for w in (*self_ptr).roots() {
-                    let id = (*w).base().unwrap().id();
+                    let id = (*w).base().id();
                     self.ui_context.register_widget(id, w);
                 }
             }
diff --git a/src/scene/arena.rs b/src/scene/arena.rs
index 917da71..c44fa40 100644
--- a/src/scene/arena.rs
+++ b/src/scene/arena.rs
@@ -571,10 +571,12 @@ mod tests {
         // else is defaulted, so this exercises the actual trait object without dragging in a
         // heavyweight widget constructor.
         struct Marker {
+            base: crate::widget::Widget,
             tint: [f32; 4],
             painted: std::cell::Cell<bool>,
         }
         impl Element for Marker {
+            crate::impl_widget_base!(Marker);
             fn color(&self) -> [f32; 4] {
                 self.painted.set(true);
                 self.tint
@@ -582,8 +584,8 @@ mod tests {
         }
 
         let mut arena: Arena<Box<dyn Element>> = Arena::new();
-        let root = arena.insert(Box::new(Marker { tint: [1.0, 0.0, 0.0, 1.0], painted: false.into() }));
-        let child = arena.insert(Box::new(Marker { tint: [0.0, 1.0, 0.0, 1.0], painted: false.into() }));
+        let root = arena.insert(Box::new(Marker { base: crate::widget::Widget::new(), tint: [1.0, 0.0, 0.0, 1.0], painted: false.into() }));
+        let child = arena.insert(Box::new(Marker { base: crate::widget::Widget::new(), tint: [0.0, 1.0, 0.0, 1.0], painted: false.into() }));
         arena.append_child(root, child);
 
         // Walk the subtree the way a paint pass will, calling a real trait method on each node.
@@ -593,8 +595,8 @@ mod tests {
             .collect();
         assert_eq!(tints, vec![[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0]]);
 
-        // The trait object is genuinely stored (its interior mutation is observable).
-        assert!(arena.value(root).unwrap().base().is_none()); // default impl still reachable
+        // The trait object is genuinely stored (its base is reachable through the box).
+        let _ = arena.value(root).unwrap().base();
 
         // Removing the root frees the child too, proving ownership lives in the arena.
         arena.remove_subtree(root);
diff --git a/src/scene/painter.rs b/src/scene/painter.rs
index be4e5ad..34f3415 100644
--- a/src/scene/painter.rs
+++ b/src/scene/painter.rs
@@ -106,7 +106,8 @@ pub fn scroll_ancestor_text_bounds(_w: &dyn Element, _ui: &UiContext) -> Option<
 /// stored on the widget base, positioned by the configured control-label layout. For
 /// legacy widgets whose only text was that label (List's columns=None frame).
 pub fn base_control_label(w: &dyn Element) -> Vec<TextLabel> {
-    if let Some(b) = w.base() {
+    {
+        let b = w.base();
         if let Some(ref label) = b.label {
             let (_, font_size) = crate::layout::control_label_font_detached_parsed();
             let color = crate::colors::control_label_color_detached_for_state(b.hovered, b.focused);
diff --git a/src/scene/tree.rs b/src/scene/tree.rs
index c0098ad..070b142 100644
--- a/src/scene/tree.rs
+++ b/src/scene/tree.rs
@@ -223,8 +223,13 @@ mod tests {
     // A minimal real `Element` so tests exercise genuine `*mut dyn Element` payloads. The boxes
     // are kept alive in a local `Vec` for the duration of each test; we hand the tree raw
     // pointers into them, mirroring how widgets (owned by the app) are referenced by the tree.
-    struct Marker(#[allow(dead_code)] u32);
+    struct Marker {
+        base: crate::widget::Widget,
+        #[allow(dead_code)]
+        tag: u32,
+    }
     impl Element for Marker {
+        crate::impl_widget_base!(Marker);
         fn color(&self) -> [f32; 4] {
             [0.0, 0.0, 0.0, 0.0]
         }
@@ -240,7 +245,7 @@ mod tests {
         }
         /// Create a widget, returning `(WidgetId, *mut dyn Element)`.
         fn make(&mut self, tag: u32) -> (WidgetId, *mut (dyn Element + 'static)) {
-            let mut b = Box::new(Marker(tag));
+            let mut b = Box::new(Marker { base: crate::widget::Widget::new(), tag: tag });
             let ptr: *mut (dyn Element + 'static) = &mut *b;
             self.boxes.push(b);
             (WidgetId(tag as usize), ptr)
diff --git a/src/widget/container/treelist.rs b/src/widget/container/treelist.rs
index e12add1..c30125c 100644
--- a/src/widget/container/treelist.rs
+++ b/src/widget/container/treelist.rs
@@ -465,7 +465,7 @@ impl TreeList {
                         self.edit_box.select_anchor = Some(0);
                         
                         let eb_ptr = self.edit_box.as_ptr_mut();
-                        let eb_id = self.edit_box.base().unwrap().id();
+                        let eb_id = self.edit_box.base().id();
                         ui.register_widget(eb_id, eb_ptr);
                         ui.link_ids(host_id, eb_id);
                         
@@ -652,23 +652,23 @@ impl Layout for TreeList {
     /// `set_parent` side effect; also heals the inline rename editor's registry entry).
     fn register_embedded_children(&mut self, host_id: WidgetId, ctx: &mut UiContext) {
         let sb_ptr = self.search_box.as_ptr_mut();
-        let sb_id = self.search_box.base().unwrap().id();
+        let sb_id = self.search_box.base().id();
         ctx.register_widget(sb_id, sb_ptr);
         ctx.link_ids(host_id, sb_id);
 
         let btn_ptr = self.add_key_btn.as_ptr_mut();
-        let btn_id = self.add_key_btn.base().unwrap().id();
+        let btn_id = self.add_key_btn.base().id();
         ctx.register_widget(btn_id, btn_ptr);
         ctx.link_ids(host_id, btn_id);
 
         let pop_ptr = self.add_key_popover_box.as_ptr_mut();
-        let pop_id = self.add_key_popover_box.base().unwrap().id();
+        let pop_id = self.add_key_popover_box.base().id();
         ctx.register_widget(pop_id, pop_ptr);
         ctx.link_ids(host_id, pop_id);
 
         if self.editing_key_idx.is_some() {
             let eb_ptr = self.edit_box.as_ptr_mut();
-            let eb_id = self.edit_box.base().unwrap().id();
+            let eb_id = self.edit_box.base().id();
             ctx.register_widget(eb_id, eb_ptr);
             ctx.link_ids(host_id, eb_id);
         }
@@ -1289,7 +1289,7 @@ mod tests {
         let mut tree_list = TreeList::new();
         tree_list.set_rect(10.0, 52.0, 380.0, 500.0);
 
-        ctx.register_widget(tree_list.base().unwrap().id(), tree_list.as_ptr_mut());
+        ctx.register_widget(tree_list.base().id(), tree_list.as_ptr_mut());
         ctx.tick(0.016);
         ctx.clear_dirty();
 
@@ -1303,7 +1303,7 @@ mod tests {
         let mut ctx = UiContext::new();
         let mut tree_list = TreeList::new();
 
-        ctx.register_widget(tree_list.base().unwrap().id(), tree_list.as_ptr_mut());
+        ctx.register_widget(tree_list.base().id(), tree_list.as_ptr_mut());
         ctx.rebuild_spatial_grid();
 
         let list_top = 52.0;
diff --git a/src/widget/core.rs b/src/widget/core.rs
index 0a65d0e..4db482b 100644
--- a/src/widget/core.rs
+++ b/src/widget/core.rs
@@ -24,8 +24,7 @@ pub mod focus {
     }
 
     pub fn set_focused(w: &mut dyn Element, ctx: Option<&mut crate::context::UiContext>) {
-        let Some(id) = w.base().map(|b| b.id()) else { return };
-        set_focused_id(id, ctx);
+        set_focused_id(w.base().id(), ctx);
     }
 
     pub fn set_focused_id(id: WidgetId, ctx: Option<&mut crate::context::UiContext>) {
@@ -41,10 +40,7 @@ pub mod focus {
     }
 
     pub fn is_focused(w: &dyn Element) -> bool {
-        match w.base() {
-            Some(b) => is_focused_id(b.id()),
-            None => false,
-        }
+        is_focused_id(w.base().id())
     }
 
     pub fn is_focused_id(id: WidgetId) -> bool {
@@ -58,9 +54,7 @@ pub mod focus {
     }
 
     pub fn clear_if_matches(w: &dyn Element) {
-        if let Some(b) = w.base() {
-            clear_if_matches_id(b.id());
-        }
+        clear_if_matches_id(w.base().id());
     }
 
     pub fn clear_if_matches_id(id: WidgetId) {
@@ -82,14 +76,12 @@ pub mod focus {
         let child_ptr = unsafe {
             std::mem::transmute::<*mut dyn Element, *mut (dyn Element + 'static)>(child as *mut dyn Element)
         };
-        if let (Some(p_base), Some(c_base)) = (parent.base(), child.base()) {
-            let (p_id, c_id) = (p_base.id(), c_base.id());
-            ctx.register_widget(p_id, parent_ptr);
-            ctx.register_widget(c_id, child_ptr);
-            // The old add_child + set_parent pair, as the tree ops they always were.
-            ctx.tree.link(p_id, c_id);
-            ctx.tree.set_parent(c_id, Some(p_id));
-        }
+        let (p_id, c_id) = (parent.base().id(), child.base().id());
+        ctx.register_widget(p_id, parent_ptr);
+        ctx.register_widget(c_id, child_ptr);
+        // The old add_child + set_parent pair, as the tree ops they always were.
+        ctx.tree.link(p_id, c_id);
+        ctx.tree.set_parent(c_id, Some(p_id));
     }
 
     /// Keyboard tree navigation from the focused widget. `ctx` resolves the focused id to a
@@ -587,7 +579,7 @@ pub mod context_menu {
     }
 
     pub fn clear_if_matches(w: &dyn Element) {
-        let Some(id) = w.base().map(|b| b.id()) else { return };
+        let id = w.base().id();
         CONTEXT_MENU.with(|m| {
             let mut menu = m.borrow_mut();
             if menu.target == Some(id) {
@@ -713,8 +705,8 @@ pub fn clear_widget_references(w: &dyn Element) {
 #[macro_export]
 macro_rules! impl_widget_base {
     ($name:ident) => {
-        fn base(&self) -> Option<&$crate::widget::Widget> { Some(&self.base) }
-        fn base_mut(&mut self) -> Option<&mut $crate::widget::Widget> { Some(&mut self.base) }
+        fn base(&self) -> &$crate::widget::Widget { &self.base }
+        fn base_mut(&mut self) -> &mut $crate::widget::Widget { &mut self.base }
         fn as_any(&self) -> &dyn std::any::Any { self }
         fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
         fn as_ptr(&self) -> *mut (dyn $crate::widget::Element + 'static) {
diff --git a/src/widget/display/serialize.rs b/src/widget/display/serialize.rs
index a12ee5c..fc0f9d7 100644
--- a/src/widget/display/serialize.rs
+++ b/src/widget/display/serialize.rs
@@ -2,9 +2,9 @@ use crate::widget::*;
 
 fn serialize_single_widget(w: &dyn Element, json: &mut String) {
     let (x, y, width, height) = w.rect();
-    let label = w.label().or_else(|| w.base().and_then(|b| b.label.clone())).unwrap_or_default();
-    let focused = w.base().map_or(false, |b| b.focused);
-    let hovered = w.base().map_or(false, |b| b.hovered);
+    let label = w.label().or_else(|| w.base().label.clone()).unwrap_or_default();
+    let focused = w.base().focused;
+    let hovered = w.base().hovered;
     let value = w.value();
     let type_name = w.type_name();
 
diff --git a/src/widget/input/ramp.rs b/src/widget/input/ramp.rs
index f9ca94f..ce7b076 100644
--- a/src/widget/input/ramp.rs
+++ b/src/widget/input/ramp.rs
@@ -375,19 +375,19 @@ impl Layout for ColorRamp {
 
     fn register_embedded_children(&mut self, host_id: WidgetId, ctx: &mut UiContext) {
         let p = self.r_slider.as_ptr_mut();
-        let id = self.r_slider.base().unwrap().id();
+        let id = self.r_slider.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
         let p = self.g_slider.as_ptr_mut();
-        let id = self.g_slider.base().unwrap().id();
+        let id = self.g_slider.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
         let p = self.b_slider.as_ptr_mut();
-        let id = self.b_slider.base().unwrap().id();
+        let id = self.b_slider.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
         let p = self.del_button.as_ptr_mut();
-        let id = self.del_button.base().unwrap().id();
+        let id = self.del_button.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
     }
@@ -758,19 +758,19 @@ impl Layout for Ramp {
 
     fn register_embedded_children(&mut self, host_id: WidgetId, ctx: &mut UiContext) {
         let p = self.preset_dropdown.as_ptr_mut();
-        let id = self.preset_dropdown.base().unwrap().id();
+        let id = self.preset_dropdown.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
         let p = self.line_type_dropdown.as_ptr_mut();
-        let id = self.line_type_dropdown.base().unwrap().id();
+        let id = self.line_type_dropdown.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
         let p = self.val_slider.as_ptr_mut();
-        let id = self.val_slider.base().unwrap().id();
+        let id = self.val_slider.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
         let p = self.del_button.as_ptr_mut();
-        let id = self.del_button.base().unwrap().id();
+        let id = self.del_button.base().id();
         ctx.register_widget(id, p);
         ctx.link_ids(host_id, id);
     }
diff --git a/src/widget/layout_helper.rs b/src/widget/layout_helper.rs
index 9945eab..21b5629 100644
--- a/src/widget/layout_helper.rs
+++ b/src/widget/layout_helper.rs
@@ -22,7 +22,7 @@ impl ColumnLayout {
     }
 
     pub fn add_widget(&mut self, widget: &mut dyn Element, height: f32) {
-        let label_off = widget.base().map_or(0.0, |b| b.label_offset());
+        let label_off = widget.base().label_offset();
         let total_h = height + label_off;
         widget.set_rect(self.x + self.margin, self.current_y, self.width - 2.0 * self.margin, height);
         self.current_y += total_h + self.gap;
@@ -36,7 +36,7 @@ impl ColumnLayout {
         let mut max_label_off = 0.0;
         for &widget_ptr in widgets {
             unsafe {
-                let off = (*widget_ptr).base().map_or(0.0, |b| b.label_offset());
+                let off = (*widget_ptr).base().label_offset();
                 if off > max_label_off {
                     max_label_off = off;
                 }
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 3e17518..b13c842 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -188,20 +188,20 @@ pub struct Size {
 }
 
 pub trait Element {
-    fn base(&self) -> Option<&Widget> { None }
-    fn base_mut(&mut self) -> Option<&mut Widget> { None }
+    /// The widget's shared base state — GUARANTEED (the flip): the `Option` escape hatch
+    /// and its `WidgetId(0)` sentinel class are gone. `Adapted` (the one production
+    /// implementor) always owns a base; test shims carry one via `impl_widget_base!`.
+    fn base(&self) -> &Widget;
+    fn base_mut(&mut self) -> &mut Widget;
     fn preferred_height(&self) -> Option<f32> { None }
 
     fn mark_dirty(&mut self, ctx: &mut UiContext) {
-        let mut parent_id = None;
-        if let Some(b) = self.base_mut() {
-            if b.dirty {
-                return;
-            }
-            b.dirty = true;
-            parent_id = b.id.get();
+        let b = self.base_mut();
+        if b.dirty {
+            return;
         }
-        if let Some(id) = parent_id {
+        b.dirty = true;
+        if let Some(id) = b.id.get() {
             if let Some(parent_ptr) = ctx.tree.parent_ptr(id) {
                 unsafe {
                     (*parent_ptr).mark_dirty(ctx);
@@ -210,32 +210,12 @@ pub trait Element {
         }
     }
 
-    fn as_any(&self) -> &dyn std::any::Any {
-        struct DummyAny;
-        static DUMMY: DummyAny = DummyAny;
-        &DUMMY
-    }
-    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
-        struct DummyAny;
-        thread_local! {
-            static DUMMY_MUT: std::cell::UnsafeCell<DummyAny> = std::cell::UnsafeCell::new(DummyAny);
-        }
-        DUMMY_MUT.with(|d| unsafe { &mut *d.get() })
-    }
-    fn as_ptr(&self) -> *mut (dyn Element + 'static) {
-        struct DummyElement;
-        impl Element for DummyElement {
-            fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
-        }
-        std::ptr::null_mut::<DummyElement>() as *mut (dyn Element + 'static)
-    }
-    fn as_ptr_mut(&mut self) -> *mut (dyn Element + 'static) {
-        struct DummyElement;
-        impl Element for DummyElement {
-            fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
-        }
-        std::ptr::null_mut::<DummyElement>() as *mut (dyn Element + 'static)
-    }
+    // Required (the flip): the old defaults manufactured DummyAny/null-DummyElement
+    // stand-ins nothing could legitimately use. `impl_widget_base!` provides all four.
+    fn as_any(&self) -> &dyn std::any::Any;
+    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
+    fn as_ptr(&self) -> *mut (dyn Element + 'static);
+    fn as_ptr_mut(&mut self) -> *mut (dyn Element + 'static);
 
     fn handle_event(&mut self, event: &Event, ctx: &mut UiContext) -> bool {
         match event {
@@ -274,15 +254,12 @@ pub trait Element {
     }
 
     fn rect(&self) -> (f32, f32, f32, f32) {
-        if let Some(b) = self.base() {
-            (b.x, b.y, b.w, b.h)
-        } else {
-            (0.0, 0.0, 0.0, 0.0)
-        }
+        let b = self.base();
+        (b.x, b.y, b.w, b.h)
     }
 
     fn label(&self) -> Option<String> {
-        self.base().and_then(|b| b.label.clone())
+        self.base().label.clone()
     }
 
     fn get_value_string(&self) -> Option<String> { None }
@@ -297,38 +274,29 @@ pub trait Element {
     }
 
     fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        if let Some(b) = self.base_mut() {
-            b.x = x;
-            b.y = y;
-            b.w = w;
-            b.h = h;
-        }
+        let b = self.base_mut();
+        b.x = x;
+        b.y = y;
+        b.w = w;
+        b.h = h;
     }
 
     fn set_row_rect(&mut self, x: f32, w: f32) {
-        if let Some(b) = self.base_mut() {
-            b.row_x = x;
-            b.row_w = w;
-        }
+        let b = self.base_mut();
+        b.row_x = x;
+        b.row_w = w;
     }
 
     fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool {
-        if ctx.is_coordinate_covered(self.base().map(|b| b.id()).unwrap_or(WidgetId(0)), px, py) {
+        if ctx.is_coordinate_covered(self.base().id(), px, py) {
             return false;
         }
         let (x, y, w, h) = self.rect();
         if w <= 0.0 || h <= 0.0 {
             return false;
         }
-        let (mut hx, mut hw) = if let Some(b) = self.base() {
-            if b.row_w > 0.0 {
-                (b.row_x, b.row_w)
-            } else {
-                (x, w)
-            }
-        } else {
-            (x, w)
-        };
+        let b = self.base();
+        let (mut hx, mut hw) = if b.row_w > 0.0 { (b.row_x, b.row_w) } else { (x, w) };
         let label_x = self.label_x_offset();
         hx += label_x;
         hw -= label_x;
@@ -337,12 +305,10 @@ pub trait Element {
 
     fn cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
         ctx.set_cursor_pos(px, py);
-        if ctx.is_coordinate_covered(self.base().map(|b| b.id()).unwrap_or(WidgetId(0)), px, py) {
-            let was = self.base().map_or(false, |b| b.hovered);
+        if ctx.is_coordinate_covered(self.base().id(), px, py) {
+            let was = self.base().hovered;
             if was {
-                if let Some(b) = self.base_mut() {
-                    b.hovered = false;
-                }
+                self.base_mut().hovered = false;
                 self.handle_event(&Event::MouseLeave, ctx);
             }
             return was;
@@ -351,22 +317,16 @@ pub trait Element {
     }
 
     fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
-        if self.base().is_some() {
-            let was = self.base().map_or(false, |b| b.hovered);
-            let is_hit = self.hit_test(px, py, ctx);
-            if let Some(b) = self.base_mut() {
-                b.hovered = is_hit;
-            }
-            if was != is_hit {
-                if is_hit {
-                    self.handle_event(&Event::MouseEnter, ctx);
-                } else {
-                    self.handle_event(&Event::MouseLeave, ctx);
-                }
-                was != is_hit
+        let was = self.base().hovered;
+        let is_hit = self.hit_test(px, py, ctx);
+        self.base_mut().hovered = is_hit;
+        if was != is_hit {
+            if is_hit {
+                self.handle_event(&Event::MouseEnter, ctx);
             } else {
-                false
+                self.handle_event(&Event::MouseLeave, ctx);
             }
+            true
         } else {
             false
         }
@@ -381,23 +341,19 @@ pub trait Element {
 
     fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, f32, [f32; 4])> {
         // Focus/hover highlight color, folded from the zero-override `highlight_color` (6bd).
-        let is_focused = self.base().map(|b| ctx.is_focused_id(b.id())).unwrap_or(false);
+        let is_focused = ctx.is_focused_id(self.base().id());
         let hc = if is_focused {
             colors::highlight_primary_color()
-        } else if self.base().map_or(false, |b| b.hovered) {
+        } else if self.base().hovered {
             colors::HIGHLIGHT_SECONDARY
         } else {
             return None;
         };
         let label_x = self.label_x_offset();
-        if let Some(b) = self.base() {
-            let hx = if b.row_w > 0.0 { b.row_x } else { b.x } + label_x;
-            let hw = if b.row_w > 0.0 { b.row_w } else { b.w } - label_x;
-            Some((hx, b.y, hw, b.h, hc))
-        } else {
-            let (x, y, w, h) = self.rect();
-            Some((x + label_x, y, w - label_x, h, hc))
-        }
+        let b = self.base();
+        let hx = if b.row_w > 0.0 { b.row_x } else { b.x } + label_x;
+        let hw = if b.row_w > 0.0 { b.row_w } else { b.w } - label_x;
+        Some((hx, b.y, hw, b.h, hc))
     }
 
     fn color(&self) -> [f32; 4];
@@ -416,7 +372,7 @@ pub trait Element {
         if name == "Label" || name == "Button" || name == "Checkbox" || name == "Toggle" || name == "Ramp" {
             return 0.0;
         }
-        if crate::layout::control_label_layout() == "side" && self.base().map_or(false, |b| b.label.is_some()) {
+        if crate::layout::control_label_layout() == "side" && self.base().label.is_some() {
             90.0
         } else {
             0.0
@@ -539,23 +495,17 @@ pub trait Element {
     fn popover_rect(&self) -> Option<(f32, f32, f32, f32)> { None }
     fn render_popover(&self, _pc: &mut dyn crate::layout::RenderTarget) {}
     fn set_text(&mut self, text: &str) {
-        if let Some(b) = self.base_mut() {
-            b.label = Some(text.to_string());
-        }
+        self.base_mut().label = Some(text.to_string());
     }
 
     fn focus(&mut self) {
-        if let Some(b) = self.base_mut() {
-            b.focused = true;
-        }
+        self.base_mut().focused = true;
     }
     fn unfocus(&mut self) {
-        if let Some(b) = self.base_mut() {
-            b.focused = false;
-        }
+        self.base_mut().focused = false;
     }
     fn focused(&self, ctx: &UiContext) -> bool {
-        self.base().map(|b| ctx.is_focused_id(b.id())).unwrap_or(false)
+        ctx.is_focused_id(self.base().id())
     }
     fn prepare_text(&mut self, _fs: &mut glyphon::FontSystem) {}
     fn set_selected(&mut self, _selected: bool) {}
@@ -569,8 +519,7 @@ pub trait Element {
     fn set_modifiers(&mut self, _ctrl: bool, _shift: bool, _alt: bool) {}
 
     fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> {
-        let base = self.base()?;
-        ctx.tree.parent_ptr(base.id())
+        ctx.tree.parent_ptr(self.base().id())
     }
 
 
@@ -579,10 +528,7 @@ pub trait Element {
     // through `focus::link_parent_child` or `ctx.tree` directly.
 
     fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'static)> {
-        match self.base() {
-            Some(base) => ctx.tree.children_ptrs(base.id()),
-            None => vec![],
-        }
+        ctx.tree.children_ptrs(self.base().id())
     }
 
     fn z_index(&self) -> i32 { 0 }
@@ -610,13 +556,11 @@ pub trait Element {
 
 pub trait Control: Element {
     fn set_label(&mut self, label: &str) {
-        if let Some(b) = self.base_mut() {
-            b.label = Some(label.to_string());
-        }
+        self.base_mut().label = Some(label.to_string());
     }
 
     fn control_label(&self) -> Option<TextLabel> {
-        let b = self.base()?;
+        let b = self.base();
         let label = b.label.as_ref()?;
         let name = self.type_name();
         
@@ -751,7 +695,7 @@ pub fn label_offset(w: &dyn Element) -> f32 {
     if name == "Label" || name == "Button" || name == "Checkbox" || name == "Toggle" {
         return 0.0;
     }
-    w.base().map_or(0.0, |b| b.label_offset())
+    w.base().label_offset()
 }
 
 #[derive(Debug, Clone, Copy, PartialEq)]
diff --git a/src/widget/model.rs b/src/widget/model.rs
index 55d9f03..7e5708b 100644
--- a/src/widget/model.rs
+++ b/src/widget/model.rs
@@ -693,24 +693,19 @@ impl<W: Layout + Paint + Input + 'static> Adapted<W> {
     /// went to `focus::link_parent_child`/tree ops).
     pub fn add_child(&mut self, child: *mut (dyn Element + 'static), ctx: &mut UiContext) {
         // The old Element default's tree link…
-        if let Some(c_base) = unsafe { (*child).base() } {
-            let c_id = c_base.id();
-            let p_id = self.base.id();
-            let self_ptr = self.as_ptr();
-            ctx.register_widget(p_id, self_ptr);
-            ctx.register_widget(c_id, child);
-            ctx.tree.link(p_id, c_id);
-        }
+        let c_id = unsafe { (*child).base().id() };
+        let p_id = self.base.id();
+        let self_ptr = self.as_ptr();
+        ctx.register_widget(p_id, self_ptr);
+        ctx.register_widget(c_id, child);
+        ctx.tree.link(p_id, c_id);
         // …plus, for containers, the legacy container extra: parent the child back (Layer,
         // Switcher) — the symmetric tree link the child's own set_parent used to make.
         if Layout::has_container_children(&self.inner) {
             let self_ptr = self.as_ptr_mut();
-            if let Some(c_base) = unsafe { (*child).base() } {
-                let c_id = c_base.id();
-                ctx.register_widget(self.base.id(), self_ptr);
-                ctx.register_widget(c_id, child);
-                ctx.tree.set_parent(c_id, Some(self.base.id()));
-            }
+            ctx.register_widget(self.base.id(), self_ptr);
+            ctx.register_widget(c_id, child);
+            ctx.tree.set_parent(c_id, Some(self.base.id()));
         }
     }
 
@@ -719,13 +714,11 @@ impl<W: Layout + Paint + Input + 'static> Adapted<W> {
         // Replica of the old Element default: symmetric tree link.
         let id = self.base.id();
         if let Some(p_ptr) = parent {
-            if let Some(p_base) = unsafe { (*p_ptr).base() } {
-                let p_id = p_base.id();
-                ctx.register_widget(p_id, p_ptr);
-                let self_ptr = self.as_ptr();
-                ctx.register_widget(id, self_ptr);
-                ctx.tree.set_parent(id, Some(p_id));
-            }
+            let p_id = unsafe { (*p_ptr).base().id() };
+            ctx.register_widget(p_id, p_ptr);
+            let self_ptr = self.as_ptr();
+            ctx.register_widget(id, self_ptr);
+            ctx.tree.set_parent(id, Some(p_id));
         } else {
             ctx.tree.set_parent(id, None);
         }
@@ -875,11 +868,11 @@ impl<W: Layout + Paint + Input + 'static> std::ops::DerefMut for Adapted<W> {
 }
 
 impl<W: Layout + Paint + Input + 'static> Element for Adapted<W> {
-    fn base(&self) -> Option<&Widget> {
-        Some(&self.base)
+    fn base(&self) -> &Widget {
+        &self.base
     }
-    fn base_mut(&mut self) -> Option<&mut Widget> {
-        Some(&mut self.base)
+    fn base_mut(&mut self) -> &mut Widget {
+        &mut self.base
     }
     // `as_any` exposes the *inner* widget: legacy code downcasts by concrete widget type
     // (`json_layout`'s `downcast_mut::<Checkbox>()`), and the adapter must be transparent to it.
@@ -923,10 +916,8 @@ impl<W: Layout + Paint + Input + 'static> Element for Adapted<W> {
             return true;
         }
         for child in Layout::container_children(&self.inner) {
-            if let Some(b) = unsafe { (*child).base() } {
-                if b.id() == child_id {
-                    return Layout::child_visible(&self.inner, child);
-                }
+            if unsafe { (*child).base().id() } == child_id {
+                return Layout::child_visible(&self.inner, child);
             }
         }
         false
@@ -1699,10 +1690,10 @@ mod tests {
         // preserves) and sets the base hover flag; moving away synthesizes MouseLeave.
         ctx.propagate_event(&Event::PointerMove { x: 20.0, y: 15.0, local_x: 20.0, local_y: 15.0 }, ptr);
         assert_eq!(w.inner().entered, 1, "MouseEnter reached on_event");
-        assert!(unsafe { (*ptr).base().map_or(false, |b| b.hovered) }, "base hover flag set through the adapter");
+        assert!(unsafe { (*ptr).base().hovered }, "base hover flag set through the adapter");
         ctx.propagate_event(&Event::PointerMove { x: 200.0, y: 200.0, local_x: 200.0, local_y: 200.0 }, ptr);
         assert_eq!(w.inner().left, 1, "MouseLeave reached on_event");
-        assert!(!unsafe { (*ptr).base().map_or(false, |b| b.hovered) }, "base hover flag cleared");
+        assert!(!unsafe { (*ptr).base().hovered }, "base hover flag cleared");
     }
 
     /// A narrow widget that is also a controller: the controller trait is reached through the