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

commit8aef319bd993eb1ba68e91103b1febc1000af63a
parentd89f72ecd1
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-08 15:37
refactor(widget): WidgetHost 38 -> 31; the legacy tuple views are WidgetHostExt

extra_quads, extra_arcs, extra_circles, all_quads, all_rounded_quads,
highlight_quad and corner_style -- what a widget paints projected onto the
pre-display-list surface -- moved to WidgetHostExt, computed from
paint_model().paint(content_rect()) exactly as Adapted computed them; Adapted's
private content_rect / painted_prims / visible_children / own_plain_quads went
with them, and Owned no longer forwards them. The hosts that still draw a
widget through these (the designer's render loop, the gallery, the display
manager's greeter, the settings app's flat collector, cce-secrets, the
flat-host bridge) call them as before; no app changed. The trait default
paint_self, which serves only model-less hosts, draws its rounded background
from its colour and corner style directly.

Checked: the workspace builds, every reader's tests pass, and the designer,
the demo, the gallery, cce-files, cce-secrets, the greeter and the settings
app drew identically to the pixel before and after (scale-2 shadow).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 CLAUDE.md                             |  12 +-
 docs/rfc-core-rebuild.md              |   7 +
 src/scene/painter.rs                  |   6 +-
 src/widget/container/menu.rs          |   5 +-
 src/widget/container/paginator.rs     |   9 +-
 src/widget/container/parameters_bg.rs |  10 +-
 src/widget/display/graph.rs           |   6 +-
 src/widget/display/progress_bar.rs    |   8 +-
 src/widget/display/separator.rs       |   2 +-
 src/widget/display/status_bar.rs      |   4 +-
 src/widget/display/status_dot.rs      |   4 +-
 src/widget/display/usage_bar.rs       |   6 +-
 src/widget/input/button.rs            |   4 +-
 src/widget/input/checkbox.rs          |   2 +-
 src/widget/input/color_selector.rs    |   2 +-
 src/widget/input/slider.rs            |   4 +-
 src/widget/input/text_box.rs          |   2 +-
 src/widget/mod.rs                     | 248 ++++++++++++++++++++++++++--------
 src/widget/model.rs                   | 194 +-------------------------
 src/widget/owned.rs                   |   9 --
 20 files changed, 241 insertions(+), 303 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index af4bf1e..6b20c7c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1671,7 +1671,7 @@ Modules:
 
 ### `WidgetHost` (formerly the `Element` god-trait)
 
-`WidgetHost` (`src/widget/mod.rs`) is the single 38-method host surface the machinery
+`WidgetHost` (`src/widget/mod.rs`) is the single 31-method host surface the machinery
 (context routing, paint walk, render loop, app dyn broadcasts) sees, produced by the RFC's 6bd
 shrink-then-rename of the old ~125-method `Element` god-trait. Its ONE production implementor
 is `Adapted<W>`; concrete widget behavior lives on the narrow `Layout`/`Paint`/`Input` traits
@@ -1681,7 +1681,7 @@ are GONE from the trait — events route through `handle_event`, and apps drain
 through the concrete inherent `Adapted<W>` methods. See the RFC's blueprint notes before
 adding anything to this trait.
 
-**What a host's widget model answers is not a trait slot** (since 2026-10-08, 57 → 38).
+**What a host's widget model answers is not a trait slot** (since 2026-10-08, 57 → 31).
 The host hands out its widget as its narrow traits — `layout_model()`, `paint_model()`,
 `input_model()` / `input_model_mut()` (`Adapted` returns its inner widget; a test shim that
 implements `WidgetHost` directly gets `NoModel`'s defaults, or returns itself after
@@ -1690,7 +1690,13 @@ host, `dyn` included, carries what used to be one-line forwards: `focus_role`, `
 `blocks_root_plate_drag`, `wants_tick`, `is_scrollable`, the `a11y_*` reads and acts,
 `set_modifiers`, `context_action`, `color`, `solid_border`, `widget_font`,
 `clips_children`, `renders_own_subtree`, `z_index`, `preferred_height`, plus the pure
-derivations `label`, `corner_radii`, `mark_dirty`. Call them as before, with
+derivations `label`, `corner_radii`, `mark_dirty` — and the legacy tuple views
+(`extra_quads`, `extra_arcs`, `extra_circles`, `all_quads`, `all_rounded_quads`,
+`highlight_quad`, `corner_style`): what a widget paints projected onto the
+pre-display-list surface, computed from `paint_model().paint(content_rect())` the way
+`Adapted` computed them, for the hosts that still draw a widget through them rather than
+`paint_self` (the designer's render loop, the gallery, the display manager's greeter, the
+settings app's flat collector, cce-secrets, the flat-host bridge). Call them as before, with
 `cce_ui::widget::WidgetHostExt` in scope. A method stays ON the trait only when the host
 adds something the model cannot (visibility gating, the content rect, child recursion,
 registry state). `plate_bevel` is gone: nothing overrode it, so it was always `None`.
diff --git a/docs/rfc-core-rebuild.md b/docs/rfc-core-rebuild.md
index e215d0e..540983b 100644
--- a/docs/rfc-core-rebuild.md
+++ b/docs/rfc-core-rebuild.md
@@ -2073,6 +2073,13 @@ Constraint respected: **each crate still builds standalone** — the new core is
     radius, is-Ramp flag) at re-parent time instead of the pointer —
     same refresh cadence, no deref of potentially-dead memory; watch
     the one-frame rect lag on resize if reads move to snapshots.
+  - **WidgetHost 38 → 31 (the same day).** The legacy tuple views (`extra_quads`,
+    `extra_arcs`, `extra_circles`, `all_quads`, `all_rounded_quads`, `highlight_quad`,
+    `corner_style`) moved to `WidgetHostExt` too, computed from the paint model exactly as
+    `Adapted` did (its private `content_rect` / `painted_prims` / `visible_children` went
+    with them). Five apps still draw through them; moving those to `paint_self` is what
+    retires the views themselves, and it changes how some look (cce-secrets draws its
+    controls flat through them). Every app that reads them drew identically to the pixel.
   - **WidgetHost 57 → 38 (2026-10-08).** The trait had grown back past the blueprint's ~55
     with the keyboard and accessibility hooks, and most of what `Adapted` implemented was a
     one-line forward to a narrow trait. The narrow traits are dyn-compatible, so the host now
diff --git a/src/scene/painter.rs b/src/scene/painter.rs
index cae6c02..b2b8aea 100644
--- a/src/scene/painter.rs
+++ b/src/scene/painter.rs
@@ -369,15 +369,15 @@ mod tests {
             fn color(&self) -> [f32; 4] {
                 [0.2, 0.4, 0.6, 1.0]
             }
+            fn corner_style(&self, _rect: Rect) -> Option<(f32, (bool, bool, bool, bool))> {
+                Some((4.0, (true, true, true, true)))
+            }
         }
         impl WidgetHost for Rounded {
             crate::impl_widget_base!(Rounded);
             fn paint_model(&self) -> &dyn crate::widget::Paint {
                 self
             }
-            fn corner_style(&self) -> (f32, (bool, bool, bool, bool)) {
-                (4.0, (true, true, true, true))
-            }
         }
         let mut ctx = UiContext::new();
         let mut w = Rounded { base: Widget::new() };
diff --git a/src/widget/container/menu.rs b/src/widget/container/menu.rs
index efa942b..3ab1262 100644
--- a/src/widget/container/menu.rs
+++ b/src/widget/container/menu.rs
@@ -22,10 +22,7 @@ use crate::colors;
 use crate::scene::layout::Rect;
 use crate::scene::paint::PaintCtx;
 use crate::widget::display::TextLabel;
-use crate::widget::{
-    Adapted, ButtonStrip, WidgetHost, ElementState, Event, EventCtx, Input, Key, Layout,
-    MenuController, MouseButton, NamedKey, PageSelector, Paint, DROPDOWN_ITEM_H,
-};
+use crate::widget::{Adapted, ButtonStrip, WidgetHost, ElementState, Event, EventCtx, Input, Key, Layout, MenuController, MouseButton, NamedKey, PageSelector, Paint, DROPDOWN_ITEM_H, WidgetHostExt};
 
 pub struct MenuBar {
     pub visible: bool,
diff --git a/src/widget/container/paginator.rs b/src/widget/container/paginator.rs
index 8a1fba3..ab27673 100644
--- a/src/widget/container/paginator.rs
+++ b/src/widget/container/paginator.rs
@@ -19,10 +19,7 @@ use crate::colors;
 use crate::scene::layout::Rect;
 use crate::scene::paint::PaintCtx;
 use crate::widget::input::ButtonStrip;
-use crate::widget::{
-    Adapted, WidgetHost, Event, EventCtx, Input, Layout, MenuController, PageSelector, Paint,
-    UiContext, WidgetId,
-};
+use crate::widget::{Adapted, WidgetHost, Event, EventCtx, Input, Layout, MenuController, PageSelector, Paint, UiContext, WidgetId, WidgetHostExt};
 
 pub struct Paginator {
     /// In an [`Owned`](crate::widget::Owned) box of its own, so the registry points at a
@@ -321,7 +318,7 @@ mod tests {
         // Legacy split: `extra_quads` is the children's chrome only; the sidebar background
         // (a rounded rect) lives in `all_rounded_quads` alone (layout-interface draws its
         // own backgrounds under `extra_quads`).
-        let extra = WidgetHost::extra_quads(&p);
+        let extra = crate::widget::WidgetHostExt::extra_quads(&p);
         let strip_extra = p.sidebar_menu.extra_quads();
         assert_eq!(extra.len(), strip_extra.len(), "children-only plain view (pages emit none)");
         let bg = colors::sidebar_bg_color();
@@ -329,7 +326,7 @@ mod tests {
             let r = crate::layout::plate_corner_radius();
             let bg_quad = (0.0, 0.0, 400.0, 300.0, r, bg, (true, true, true, true));
             assert!(!extra.iter().any(|q| q.4 == bg && q.2 == 400.0), "no own bg in extra_quads");
-            assert!(WidgetHost::all_rounded_quads(&p, &ctx).contains(&bg_quad), "own bg in all_rounded_quads");
+            assert!(crate::widget::WidgetHostExt::all_rounded_quads(&p, &ctx).contains(&bg_quad), "own bg in all_rounded_quads");
         }
 
         // The embedded strip + pages land in the registry on tick (the spatial grid feeds off
diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index 670f38a..b1b51d8 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -38,10 +38,8 @@ use crate::scene::paint::PaintCtx;
 use crate::widget::display::{Float3, TextLabel};
 use crate::scene::paint::Field;
 use crate::widget::input::{Button, ColorSelector, Dropdown, Ramp, Slider, Spinbox, TextBox, Toggle};
-use crate::widget::{
-    Adapted, WidgetHost, ElementState, Event, EventCtx, Input, Key, Layout, MouseButton,
-    MouseScrollDelta, NamedKey, Paint, ParamController, TextEditorState, UiContext,
-};
+use crate::widget::{Adapted, WidgetHost, ElementState, Event, EventCtx, Input, Key, Layout, MouseButton,
+    MouseScrollDelta, NamedKey, Paint, ParamController, TextEditorState, UiContext, WidgetHostExt};
 
 /// A text row, in either variant: plain (`"text"`), or with a completion
 /// picker (`"textpick:a,b,c"` — the Houdini-style attribute/group chooser: a
@@ -4728,7 +4726,7 @@ mod tests {
         let p = panel_with(&[("Size", "1.00", "slider:0:2")]);
         // The designer's plain path: extra_quads carries the row chrome (clipped), including
         // the slider background it reads via rect()+color()...
-        let extra = WidgetHost::extra_quads(&p);
+        let extra = crate::widget::WidgetHostExt::extra_quads(&p);
         assert!(!extra.is_empty(), "row chrome served through extra_quads");
         // ...but NOT the panel's own PARAM_BG plate (the host draws that from color()).
         let (x, y, w, h) = WidgetHost::rect(&p);
@@ -4737,7 +4735,7 @@ mod tests {
             "panel bg plate is the host's, not extra_quads'"
         );
         // The no-double-draw contract of the plain-quad hatch.
-        assert!(WidgetHost::all_quads(&p, &ctx).is_empty());
+        assert!(crate::widget::WidgetHostExt::all_quads(&p, &ctx).is_empty());
         // Per-label hatch: the walk's text prims carry the widget font and viewport bounds.
         let mut scratch = crate::scene::paint::PaintCtx::new();
         crate::scene::painter::append_widget_text(&ctx, &p, &mut scratch);
diff --git a/src/widget/display/graph.rs b/src/widget/display/graph.rs
index 1810af6..55560a3 100644
--- a/src/widget/display/graph.rs
+++ b/src/widget/display/graph.rs
@@ -2271,8 +2271,8 @@ mod tests {
 
         // The plain view (designer path) and the rounded view (render_widget path) describe
         // the same quads: the rounded view adds only the widget background entry up front.
-        let plain = WidgetHost::extra_quads(&g);
-        let rounded = WidgetHost::all_rounded_quads(&g, &ctx);
+        let plain = crate::widget::WidgetHostExt::extra_quads(&g);
+        let rounded = crate::widget::WidgetHostExt::all_rounded_quads(&g, &ctx);
         assert!(!plain.is_empty());
         assert_eq!(rounded.len(), plain.len() + 1);
         for ((px, py, pw, ph, pc), (rx, ry, rw, rh, _, rc, _)) in plain.iter().zip(rounded.iter().skip(1)) {
@@ -2293,6 +2293,6 @@ mod tests {
 
         // And `all_quads` stays empty so render_widget hosts (reading BOTH getters) never
         // draw the geometry twice — the legacy Graph override's contract.
-        assert!(WidgetHost::all_quads(&g, &ctx).is_empty());
+        assert!(crate::widget::WidgetHostExt::all_quads(&g, &ctx).is_empty());
     }
 }
diff --git a/src/widget/display/progress_bar.rs b/src/widget/display/progress_bar.rs
index 06a01ba..9c8ba89 100644
--- a/src/widget/display/progress_bar.rs
+++ b/src/widget/display/progress_bar.rs
@@ -106,7 +106,7 @@ mod tests {
         let mut bar = ProgressBar::new(0.5).with_recessed(false);
         WidgetHost::set_rect(&mut bar, 10.0, 20.0, 100.0, 8.0);
 
-        let quads = WidgetHost::all_rounded_quads(&bar, &ctx);
+        let quads = crate::widget::WidgetHostExt::all_rounded_quads(&bar, &ctx);
         let radius = crate::layout::slider_corner_radius();
         assert_eq!(quads.len(), 2, "track + fill");
         assert_eq!(quads[0], (10.0, 20.0, 100.0, 8.0, radius, colors::progress_bg(), (true, true, true, true)));
@@ -122,12 +122,12 @@ mod tests {
         let ctx = UiContext::new();
         let mut over = ProgressBar::new(2.0).with_recessed(false);
         WidgetHost::set_rect(&mut over, 0.0, 0.0, 100.0, 8.0);
-        let quads = WidgetHost::all_rounded_quads(&over, &ctx);
+        let quads = crate::widget::WidgetHostExt::all_rounded_quads(&over, &ctx);
         assert_eq!(quads[1].2, 100.0, "over-1 value fills the whole track");
 
         let mut empty = ProgressBar::new(0.0).with_recessed(false);
         WidgetHost::set_rect(&mut empty, 0.0, 0.0, 100.0, 8.0);
-        assert_eq!(WidgetHost::all_rounded_quads(&empty, &ctx).len(), 1, "zero value emits track only");
+        assert_eq!(crate::widget::WidgetHostExt::all_rounded_quads(&empty, &ctx).len(), 1, "zero value emits track only");
     }
 
     /// The recessed style draws no track of its own: the fill on the well floor, then
@@ -162,7 +162,7 @@ mod tests {
         assert_eq!(h, 8.0 + offset, "the rect is the block it was given");
         assert_eq!(y, 10.0, "origin is unchanged");
 
-        let quads = WidgetHost::all_rounded_quads(&bar, &ctx);
+        let quads = crate::widget::WidgetHostExt::all_rounded_quads(&bar, &ctx);
         assert_eq!(quads[0].1, 10.0 + offset, "track is painted below the label region");
         assert_eq!(quads[0].3, 8.0, "track keeps the assigned height");
 
diff --git a/src/widget/display/separator.rs b/src/widget/display/separator.rs
index 62d3f59..520ca7f 100644
--- a/src/widget/display/separator.rs
+++ b/src/widget/display/separator.rs
@@ -89,7 +89,7 @@ mod tests {
         assert!(!crate::widget::WidgetHostExt::blocks_root_plate_drag(&sep));
         if !crate::layout::control_relief() {
             assert_eq!(crate::widget::WidgetHostExt::color(&sep), [0.3, 0.3, 0.3, 1.0]);
-            assert_eq!(WidgetHost::extra_quads(&sep), vec![(100.0, 0.0, 1.0, 24.0, [0.3, 0.3, 0.3, 1.0])]);
+            assert_eq!(crate::widget::WidgetHostExt::extra_quads(&sep), vec![(100.0, 0.0, 1.0, 24.0, [0.3, 0.3, 0.3, 1.0])]);
         }
     }
 
diff --git a/src/widget/display/status_bar.rs b/src/widget/display/status_bar.rs
index b97a3a0..947199f 100644
--- a/src/widget/display/status_bar.rs
+++ b/src/widget/display/status_bar.rs
@@ -232,9 +232,9 @@ mod tests {
         assert_eq!(bar.text, "world");
 
         // Parentless: cornerless plain bg through the plain-quad bridge, at STATUS_BG.
-        let extra = WidgetHost::extra_quads(&bar);
+        let extra = crate::widget::WidgetHostExt::extra_quads(&bar);
         assert_eq!(extra.len(), 1, "cornerless bg quad");
-        assert_eq!(WidgetHost::corner_style(&bar).1, (false, false, false, false));
+        assert_eq!(crate::widget::WidgetHostExt::corner_style(&bar).1, (false, false, false, false));
         assert!(!crate::widget::WidgetHostExt::blocks_root_plate_drag(&bar));
     }
 
diff --git a/src/widget/display/status_dot.rs b/src/widget/display/status_dot.rs
index 8c0370e..dfa5526 100644
--- a/src/widget/display/status_dot.rs
+++ b/src/widget/display/status_dot.rs
@@ -79,11 +79,11 @@ mod tests {
         let mut dot = StatusDot::new(DotStatus::Warning);
         WidgetHost::set_rect(&mut dot, 5.0, 6.0, 10.0, 10.0);
         assert_eq!(
-            WidgetHost::all_rounded_quads(&dot, &UiContext::new()),
+            crate::widget::WidgetHostExt::all_rounded_quads(&dot, &UiContext::new()),
             vec![(5.0, 6.0, 10.0, 10.0, 5.0, [0.90, 0.60, 0.10, 1.0], (true, true, true, true))],
             "a disc: the rect at half-side radius",
         );
-        assert!(WidgetHost::extra_quads(&dot).is_empty(), "nothing on the plain path (apps read both)");
+        assert!(crate::widget::WidgetHostExt::extra_quads(&dot).is_empty(), "nothing on the plain path (apps read both)");
         // Drags pass through, as legacy declared.
         assert!(!crate::widget::WidgetHostExt::blocks_root_plate_drag(&dot));
         // State mutation through Deref, as call sites write it.
diff --git a/src/widget/display/usage_bar.rs b/src/widget/display/usage_bar.rs
index 46ab580..4becbcb 100644
--- a/src/widget/display/usage_bar.rs
+++ b/src/widget/display/usage_bar.rs
@@ -110,13 +110,13 @@ mod tests {
         let radius = crate::layout::slider_corner_radius();
         let all = (true, true, true, true);
         assert_eq!(
-            WidgetHost::all_rounded_quads(&bar, &crate::widget::UiContext::new()),
+            crate::widget::WidgetHostExt::all_rounded_quads(&bar, &crate::widget::UiContext::new()),
             vec![
                 (12.0, 30.0, 200.0, 8.0, radius, [0.4, 0.5, 0.6, 1.0], all),
                 (12.0, 30.0, 100.0, 8.0, radius.min(4.0), [0.1, 0.2, 0.3, 1.0], all),
             ],
         );
-        assert!(WidgetHost::extra_quads(&bar).is_empty(), "no plain quad leaks to flat hosts");
+        assert!(crate::widget::WidgetHostExt::extra_quads(&bar).is_empty(), "no plain quad leaks to flat hosts");
     }
 
     /// Recessed: no bg quad at all — the fill on the floor, then the carve.
@@ -130,7 +130,7 @@ mod tests {
         assert_eq!(prims.len(), 2, "{prims:?}");
         assert!(matches!(prims[0], Prim::RoundedRect { .. }));
         assert!(matches!(prims[1], Prim::Recess { .. }));
-        assert!(WidgetHost::extra_quads(&bar).is_empty(), "no plain bg quad leaks to flat hosts");
+        assert!(crate::widget::WidgetHostExt::extra_quads(&bar).is_empty(), "no plain bg quad leaks to flat hosts");
     }
 
     #[test]
diff --git a/src/widget/input/button.rs b/src/widget/input/button.rs
index 50b17bc..8413391 100644
--- a/src/widget/input/button.rs
+++ b/src/widget/input/button.rs
@@ -846,8 +846,8 @@ mod tests {
         let b = Button::new(0.0, 0.0, 100.0, 24.0).with_label("Go").with_raised(false);
 
         let radius = crate::layout::button_corner_radius();
-        let rounded = WidgetHost::all_rounded_quads(&b, &ctx);
-        let plain = WidgetHost::extra_quads(&b);
+        let rounded = crate::widget::WidgetHostExt::all_rounded_quads(&b, &ctx);
+        let plain = crate::widget::WidgetHostExt::extra_quads(&b);
         if radius > 0.0 {
             assert!(!rounded.is_empty() && plain.is_empty(), "rounded config -> rounded path only");
             assert_eq!(rounded[0].4, radius);
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index f980aea..d6362f8 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -707,7 +707,7 @@ mod tests {
         let (fcx, fcy) = (f.rect.x + f.rect.width * 0.5, f.rect.y + f.rect.height * 0.5);
         assert!((pcx - fcx).abs() < 1e-3 && (pcy - fcy).abs() < 1e-3, "centred");
         assert!(!p.has_well(), "the plate is all run");
-        assert!(WidgetHost::extra_circles(&cb).is_empty(), "no mark");
+        assert!(crate::widget::WidgetHostExt::extra_circles(&cb).is_empty(), "no mark");
 
         if crate::layout::control_relief() {
             cb.inner_mut().set_checked(false);
diff --git a/src/widget/input/color_selector.rs b/src/widget/input/color_selector.rs
index 12a55b6..061d750 100644
--- a/src/widget/input/color_selector.rs
+++ b/src/widget/input/color_selector.rs
@@ -1094,7 +1094,7 @@ mod tests {
             !items.iter().any(|it| matches!(it.prim, Prim::Quad { color, .. } if color == [1.0; 4])),
             "no cell is a plain quad, so the legacy plain view cannot carry it"
         );
-        let plain = crate::widget::WidgetHost::extra_quads(&cs);
+        let plain = crate::widget::WidgetHostExt::extra_quads(&cs);
         assert!(plain.iter().all(|q| q.4 != [1.0, 1.0, 1.0, 1.0]), "the plain view has no white cells: {plain:?}");
     }
 
diff --git a/src/widget/input/slider.rs b/src/widget/input/slider.rs
index e552c1d..ba11444 100644
--- a/src/widget/input/slider.rs
+++ b/src/widget/input/slider.rs
@@ -1124,8 +1124,8 @@ fn probe_slider_bridge() {
     let mut sl = Slider::new().with_label("Slider");
     WidgetHost::set_rect(&mut sl, 20.0, 220.0, 200.0, 40.0);
     eprintln!("rect         = {:?}", WidgetHost::rect(&sl));
-    eprintln!("extra_quads  = {:?}", WidgetHost::extra_quads(&sl));
-    eprintln!("rounded      = {:?}", WidgetHost::all_rounded_quads(&sl, &ctx));
+    eprintln!("extra_quads  = {:?}", crate::widget::WidgetHostExt::extra_quads(&sl));
+    eprintln!("rounded      = {:?}", crate::widget::WidgetHostExt::all_rounded_quads(&sl, &ctx));
     eprintln!("labels       = {:?}", sl.own_text_labels().iter().map(|l| l.text.clone()).collect::<Vec<_>>());
 }
 
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index a099575..91dca63 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -2817,7 +2817,7 @@ mod tests {
         tb.select_anchor = Some(7); // starts at "Line 2"
         tb.cursor_idx = 13;        // ends at end of "Line 2"
 
-        let has_rounded = WidgetHost::corner_style(&tb).1 != (false, false, false, false);
+        let has_rounded = crate::widget::WidgetHostExt::corner_style(&tb).1 != (false, false, false, false);
         let has_highlight = if has_rounded {
             let rounded = tb.all_rounded_quads(&dummy);
             println!("Rounded quads: {:?}", rounded);
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 02098a9..4da0d65 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -379,21 +379,6 @@ pub trait WidgetHost {
     // `Widget::hovered` flag, read/written directly by the defaults above; Button/Checkbox
     // keep inherent accessors for immediate-mode hosts.
 
-    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 = ctx.is_focused_id(self.base().id());
-        let hc = if is_focused {
-            colors::highlight_primary_color()
-        } else if self.base().hovered {
-            colors::HIGHLIGHT_SECONDARY
-        } else {
-            return None;
-        };
-        let b = self.base();
-        let hx = if b.row_w > 0.0 { b.row_x } else { b.x };
-        let hw = if b.row_w > 0.0 { b.row_w } else { b.w };
-        Some((hx, b.y, hw, b.h, hc))
-    }
 
 
     // `draggable`/`is_dragging` are GONE from the trait (the ControlPanel endgame
@@ -401,19 +386,7 @@ pub trait WidgetHost {
     // inherent `Adapted<W>` reads; index-driven rosters (TI, designer) route them
     // through per-slot matches like the other value drains.
 
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> { Vec::new() }
-    fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> { Vec::new() }
-    fn extra_circles(&self) -> Vec<(f32, f32, f32, [f32; 4])> { Vec::new() }
     
-    fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = self.extra_quads();
-        if let Some(hq) = self.highlight_quad(ctx) {
-            if hq.4 != colors::HIGHLIGHT_SECONDARY {
-                quads.push(hq);
-            }
-        }
-        quads
-    }
 
     /// Emit this widget's OWN primitives (non-recursive) into the single paint pass (Phase 3).
     /// The default composes the pieces the legacy recursive `all_*` emit for one node: rounded
@@ -427,10 +400,13 @@ pub trait WidgetHost {
         let color = self.color();
 
         if ui.tree.children_ptrs(self.base().id()).is_empty() {
-            // Leaf: emit its own rounded quads directly. For an ordinary widget this is just the
-            // rounded background; for widgets that override `all_rounded_quads` with custom
-            // geometry (e.g. Graph's nodes and edges) it captures that too. No recursion happens
-            // because there are no children.
+            // Leaf: its rounded background, from its colour and corner style, then the rounded
+            // quads its model paints. (Every production host overrides `paint_self`; this
+            // default serves a host without a model of its own.)
+            let (radius, corners) = self.corner_style();
+            if corners != (false, false, false, false) && color[3].abs() > 0.001 {
+                ctx.rounded_rect(rect, radius, corners, color);
+            }
             for (qx, qy, qw, qh, r, c, corners) in self.all_rounded_quads(ui) {
                 ctx.rounded_rect(Rect { x: qx, y: qy, width: qw, height: qh }, r, corners, c);
             }
@@ -468,25 +444,6 @@ pub trait WidgetHost {
 
 
 
-    fn all_rounded_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))> {
-        if !self.visible() {
-            return Vec::new();
-        }
-        let mut quads = Vec::new();
-        let (radius, (r1, r2, r3, r4)) = self.corner_style();
-        if r1 || r2 || r3 || r4 {
-            let (x, y, w, h) = self.rect();
-            let c = self.color();
-            if c[3].abs() > 0.001 {
-                quads.push((x, y, w, h, radius, c, (r1, r2, r3, r4)));
-            }
-        }
-        for &child_ptr in &ctx.tree.children_ptrs(self.base().id()) {
-            let widget = unsafe { &*child_ptr };
-            quads.extend(widget.all_rounded_quads(ctx));
-        }
-        quads
-    }
 
 
     // The per-widget text getters (text_labels / text_labels_with_bounds /
@@ -529,13 +486,6 @@ pub trait WidgetHost {
     // the tree link its per-tick `register_embedded_children` maintains.
 
 
-    /// Uniform corner radius + per-corner on-flags, in one read (6bd batch 2 — replaced the
-    /// separate `corner_radius`/`rounded_corners` getters). The radius is meaningful even with
-    /// every corner off: Menu/StatusBar report their parent's radius to children this way, so
-    /// the flags-off channel can't be folded into `corner_radii`.
-    fn corner_style(&self) -> (f32, (bool, bool, bool, bool)) {
-        (12.0, (false, false, false, false))
-    }
 
 
 
@@ -689,6 +639,190 @@ pub trait WidgetHostExt: WidgetHost {
             }
         }
     }
+
+    // ── The legacy tuple views ──────────────────────────────────────────────────
+    // What a widget paints, projected onto the pre-display-list surface: plain quads,
+    // rounded quads, arcs, circles, the shared highlight, the corner style. Hosts that
+    // still draw a widget through these rather than `paint_self` — the designer's render
+    // loop, the gallery, the display manager, the settings app's flat collector,
+    // cce-secrets, the flat-host bridge — read them here. They were `WidgetHost` methods
+    // until 2026-10-08, each implemented by `Adapted` from its widget's `Paint`; they are
+    // computed from the models now, the same way.
+
+    /// The rect the widget's model paints into: the host's rect below its detached label.
+    fn content_rect(&self) -> crate::scene::layout::Rect {
+        let b = self.base();
+        let top = if self.layout_model().inline_label() { 0.0 } else { b.label_offset() };
+        // Deliberately NOT clamped at zero: hosts under-size labeled sliders (label taller
+        // than the assigned rect), and the negative-height quads still rasterize.
+        crate::scene::layout::Rect { x: b.x, y: b.y + top, width: b.w, height: b.h - top }
+    }
+
+    /// Everything the widget's model paints into [`content_rect`](Self::content_rect), as prims.
+    fn painted_prims(&self) -> Vec<crate::scene::paint::Prim> {
+        let mut pc = crate::scene::paint::PaintCtx::new();
+        self.paint_model().paint(self.content_rect(), &mut pc);
+        pc.finish().items.into_iter().map(|item| item.prim).collect()
+    }
+
+    /// The container's children that pass its [`Layout::child_visible`] policy; empty for a
+    /// non-container.
+    fn visible_children(&self) -> Vec<*mut (dyn WidgetHost + 'static)> {
+        let model = self.layout_model();
+        if !model.has_container_children() {
+            return Vec::new();
+        }
+        model.container_children().into_iter().filter(|c| model.child_visible(*c)).collect()
+    }
+
+    /// The corner radius and which corners it rounds; 12 and none for a widget without a
+    /// corner style.
+    fn corner_style(&self) -> (f32, (bool, bool, bool, bool)) {
+        self.paint_model().corner_style(self.content_rect()).unwrap_or((12.0, (false, false, false, false)))
+    }
+
+    /// The legacy hover/focus highlight over the row span: only for a widget that opts into
+    /// it ([`Paint::legacy_focus_highlight`], the TextBox) or forwards one
+    /// ([`Paint::forwarded_highlight`], the Paginator's ButtonStrip). Primary tint when
+    /// focused, secondary when hovered.
+    fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, f32, [f32; 4])> {
+        if let Some(forwarded) = self.paint_model().forwarded_highlight(ctx) {
+            return forwarded;
+        }
+        if !self.paint_model().legacy_focus_highlight() {
+            return None;
+        }
+        let b = self.base();
+        let hc = if ctx.is_focused_id(b.id()) {
+            colors::highlight_primary_color()
+        } else if b.hovered {
+            colors::HIGHLIGHT_SECONDARY
+        } else {
+            return None;
+        };
+        let hx = if b.row_w > 0.0 { b.row_x } else { b.x };
+        let hw = if b.row_w > 0.0 { b.row_w } else { b.w };
+        Some((hx, b.y, hw, b.h, hc))
+    }
+
+    /// The widget's own plain-quad prims.
+    fn own_plain_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+        self.painted_prims()
+            .into_iter()
+            .filter_map(|prim| match prim {
+                crate::scene::paint::Prim::Quad { rect, color } => Some((rect.x, rect.y, rect.width, rect.height, color)),
+                _ => None,
+            })
+            .collect()
+    }
+
+    /// The plain-quad view: a widget's own plain quads, or the legacy view it serves
+    /// ([`Paint::legacy_plain_quads`]), or — for an aggregating container (the Paginator) —
+    /// its visible children's.
+    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+        if !self.visible() {
+            return Vec::new();
+        }
+        if self.paint_model().serves_legacy_plain_quads() {
+            return self.paint_model().legacy_plain_quads(self.content_rect());
+        }
+        if self.paint_model().aggregates_child_extra_quads() {
+            let mut out = Vec::new();
+            for child in self.visible_children() {
+                out.extend(unsafe { &*child }.extra_quads());
+            }
+            return out;
+        }
+        self.own_plain_quads()
+    }
+
+    /// Every plain quad the widget and its visible children draw, the opted-in highlight
+    /// included (not the hover tint), a rounded child's background left to
+    /// [`all_rounded_quads`](Self::all_rounded_quads). Empty for a widget serving the
+    /// legacy plain-quad view, which reaches flat hosts through `all_rounded_quads` only.
+    fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+        if self.paint_model().serves_legacy_plain_quads() {
+            return Vec::new();
+        }
+        let mut quads = if self.visible() { self.own_plain_quads() } else { Vec::new() };
+        if self.paint_model().legacy_focus_highlight() {
+            if let Some(hq) = self.highlight_quad(ctx) {
+                if hq.4 != colors::HIGHLIGHT_SECONDARY {
+                    quads.push(hq);
+                }
+            }
+        }
+        if self.visible() {
+            for child in self.visible_children() {
+                let widget = unsafe { &*child };
+                let (wx, wy, ww, wh) = widget.rect();
+                let has_rounded = widget.corner_style().1 != (false, false, false, false);
+                for (qx, qy, qw, qh, qc) in widget.all_quads(ctx) {
+                    if has_rounded
+                        && (qx - wx).abs() < 0.1
+                        && (qy - wy).abs() < 0.1
+                        && (qw - ww).abs() < 0.1
+                        && (qh - wh).abs() < 0.1
+                    {
+                        continue;
+                    }
+                    quads.push((qx, qy, qw, qh, qc));
+                }
+            }
+        }
+        quads
+    }
+
+    /// Every rounded quad the widget and its visible children draw.
+    fn all_rounded_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))> {
+        if !self.visible() {
+            return Vec::new();
+        }
+        let mut out: Vec<_> = self
+            .painted_prims()
+            .into_iter()
+            .filter_map(|prim| match prim {
+                crate::scene::paint::Prim::RoundedRect { rect, radius, corners, color } => {
+                    Some((rect.x, rect.y, rect.width, rect.height, radius, color, corners))
+                }
+                _ => None,
+            })
+            .collect();
+        for child in self.visible_children() {
+            out.extend(unsafe { &*child }.all_rounded_quads(ctx));
+        }
+        out
+    }
+
+    /// The widget's arc prims.
+    fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> {
+        if !self.visible() {
+            return Vec::new();
+        }
+        self.painted_prims()
+            .into_iter()
+            .filter_map(|prim| match prim {
+                crate::scene::paint::Prim::Arc { cx, cy, radius, thickness, start, end, color } => {
+                    Some((cx, cy, radius, thickness, start, end, color))
+                }
+                _ => None,
+            })
+            .collect()
+    }
+
+    /// The widget's circle prims.
+    fn extra_circles(&self) -> Vec<(f32, f32, f32, [f32; 4])> {
+        if !self.visible() {
+            return Vec::new();
+        }
+        self.painted_prims()
+            .into_iter()
+            .filter_map(|prim| match prim {
+                crate::scene::paint::Prim::Circle { cx, cy, radius, color } => Some((cx, cy, radius, color)),
+                _ => None,
+            })
+            .collect()
+    }
 }
 
 impl<T: WidgetHost + ?Sized> WidgetHostExt for T {}
diff --git a/src/widget/model.rs b/src/widget/model.rs
index 86a77c2..4993467 100644
--- a/src/widget/model.rs
+++ b/src/widget/model.rs
@@ -804,22 +804,6 @@ impl<W: Layout + Paint + Input + 'static> Adapted<W> {
         Paint::sync_label(&mut self.inner, text);
     }
 
-    /// The rect the wrapped widget paints into: the widget's rect minus the detached-label
-    /// strip at the top (zero when there is no label, or when the widget's label is its
-    /// content — [`Layout::inline_label`]).
-    fn content_rect(&self) -> Rect {
-        let top = if Layout::inline_label(&self.inner) { 0.0 } else { self.base.label_offset() };
-        Rect {
-            x: self.base.x,
-            y: self.base.y + top,
-            width: self.base.w,
-            // Deliberately NOT clamped at zero: legacy geometry computed `h - label_offset`
-            // raw, and hosts under-size labeled sliders (label taller than the assigned rect);
-            // the resulting negative-height quads still rasterize (flipped), which is what
-            // keeps those tracks visible. Clamping made them vanish — found the hard way.
-            height: self.base.h - top,
-        }
-    }
 }
 
 impl<W: Layout + Paint + Input + 'static> Adapted<W> {
@@ -982,41 +966,8 @@ impl<W: Layout + Paint + Input + 'static> Adapted<W> {
         Layout::intrinsic_size(&self.inner)
     }
 
-    /// Run the wrapped widget's [`Paint::paint`] against its content rect and return the emitted
-    /// prims — the shared source for the reverse bridges (`extra_quads`, `all_rounded_quads`,
-    /// `extra_circles`, `extra_arcs`, prim-derived `text_labels`) that legacy render loops read.
-    fn painted_prims(&self) -> Vec<Prim> {
-        let mut pc = PaintCtx::new();
-        Paint::paint(&self.inner, self.content_rect(), &mut pc);
-        pc.finish().items.into_iter().map(|item| item.prim).collect()
-    }
 
-    /// This widget's OWN plain-quad prims from [`Paint::paint`] — the shared source for the
-    /// `extra_quads`/`all_quads` reverse bridges (kept separate from `extra_quads` itself,
-    /// which may serve the child aggregation instead —
-    /// [`Paint::aggregates_child_extra_quads`]).
-    fn own_plain_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        self.painted_prims()
-            .into_iter()
-            .filter_map(|prim| match prim {
-                Prim::Quad { rect, color } => Some((rect.x, rect.y, rect.width, rect.height, color)),
-                _ => None,
-            })
-            .collect()
-    }
 
-    /// The container's children that pass the [`Layout::child_visible`] policy — the set the
-    /// adapter's subtree plumbing (aggregation, recursion, hit-through) operates on. Empty for
-    /// non-containers.
-    fn visible_children(&self) -> Vec<*mut (dyn WidgetHost + 'static)> {
-        if !Layout::has_container_children(&self.inner) {
-            return Vec::new();
-        }
-        Layout::container_children(&self.inner)
-            .into_iter()
-            .filter(|c| Layout::child_visible(&self.inner, *c))
-            .collect()
-    }
 
     /// This widget's OWN text (prim-derived + detached base label), before any child
     /// aggregation — the shared source for the three text getters.
@@ -1290,34 +1241,6 @@ impl<W: Layout + Paint + Input + 'static> WidgetHost for Adapted<W> {
         }
     }
 
-    /// Narrow widgets own every pixel they draw through [`Paint::paint`]; the legacy shared
-    /// hover-highlight overlay is suppressed (matching what most control widgets' `None`
-    /// overrides do today) — unless the widget opts back in
-    /// ([`Paint::legacy_focus_highlight`], TextBox), in which case this replicates the
-    /// `WidgetHost` default byte-for-byte: primary tint when ctx-focused (or active), secondary
-    /// when hovered, over the row-substituted span.
-    fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, f32, [f32; 4])> {
-        // A forwarding widget (Paginator → its ButtonStrip) serves the forwarded value here —
-        // and only here; `all_quads`/`paint_self` gate on `legacy_focus_highlight` instead, so
-        // the forwarded quad is never double-drawn.
-        if let Some(forwarded) = Paint::forwarded_highlight(&self.inner, ctx) {
-            return forwarded;
-        }
-        if !Paint::legacy_focus_highlight(&self.inner) {
-            return None;
-        }
-        let is_focused = ctx.is_focused_id(self.base.id());
-        let hc = if is_focused {
-            crate::colors::highlight_primary_color()
-        } else if self.base.hovered {
-            crate::colors::HIGHLIGHT_SECONDARY
-        } else {
-            return None;
-        };
-        let hx = if self.base.row_w > 0.0 { self.base.row_x } else { self.base.x };
-        let hw = if self.base.row_w > 0.0 { self.base.row_w } else { self.base.w };
-        Some((hx, self.base.y, hw, self.base.h, hc))
-    }
 
     /// Report the *inner* type's name, not `Adapted<W>`: runtime type-name matching (e.g.
     /// `layout.rs`' span-full widget list) must keep seeing the widget it knows.
@@ -1326,11 +1249,6 @@ impl<W: Layout + Paint + Input + 'static> WidgetHost for Adapted<W> {
     }
 
     // --- Paint concern -> `Paint` ---
-    fn corner_style(&self) -> (f32, (bool, bool, bool, bool)) {
-        // 12.0 / all-off mirrors the `WidgetHost` default for widgets without a corner style.
-        Paint::corner_style(&self.inner, self.content_rect())
-            .unwrap_or((12.0, (false, false, false, false)))
-    }
 
 
 
@@ -1383,7 +1301,7 @@ impl<W: Layout + Paint + Input + 'static> WidgetHost for Adapted<W> {
         // highlight — replicate for opt-in widgets, over the background (same draw order).
         // Forwarded highlights stay out: the paint walk reaches the owning child itself.
         if Paint::legacy_focus_highlight(&self.inner) {
-            if let Some((hx, hy, hw, hh, hc)) = WidgetHost::highlight_quad(self, ui) {
+            if let Some((hx, hy, hw, hh, hc)) = crate::widget::WidgetHostExt::highlight_quad(self, ui) {
                 if hc != crate::colors::HIGHLIGHT_SECONDARY {
                     ctx.quad(Rect { x: hx, y: hy, width: hw, height: hh }, hc);
                 }
@@ -1436,120 +1354,10 @@ impl<W: Layout + Paint + Input + 'static> WidgetHost for Adapted<W> {
     // the widget's OWN geometry only: adapted widgets are leaves for now; recursion belongs to
     // `scene::painter`.
 
-    fn all_rounded_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))> {
-        if !self.visible() {
-            return Vec::new();
-        }
-        let mut out: Vec<_> = self
-            .painted_prims()
-            .into_iter()
-            .filter_map(|prim| match prim {
-                Prim::RoundedRect { rect, radius, corners, color } => {
-                    Some((rect.x, rect.y, rect.width, rect.height, radius, color, corners))
-                }
-                _ => None,
-            })
-            .collect();
-        // Containers recurse, matching the `WidgetHost` default this override replaces.
-        for child in self.visible_children() {
-            out.extend(unsafe { &*child }.all_rounded_quads(ctx));
-        }
-        out
-    }
 
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        if !self.visible() {
-            return Vec::new();
-        }
-        if Paint::serves_legacy_plain_quads(&self.inner) {
-            return Paint::legacy_plain_quads(&self.inner, self.content_rect());
-        }
-        // Legacy container aggregation (Paginator): the plain view is the visible children's
-        // chrome, and only that — the widget's own background stays in `all_quads`.
-        if Paint::aggregates_child_extra_quads(&self.inner) {
-            let mut out = Vec::new();
-            for child in self.visible_children() {
-                out.extend(unsafe { &*child }.extra_quads());
-            }
-            return out;
-        }
-        self.own_plain_quads()
-    }
 
-    /// When the widget serves a legacy plain-quad view, its geometry reaches
-    /// `render_widget`-style hosts (which read BOTH quad getters) through `all_rounded_quads`
-    /// only — `all_quads` must stay empty or they draw it twice. Mirrors legacy Graph's
-    /// highlight-only `all_quads` override. Otherwise: the `WidgetHost` default minus the shared
-    /// highlight (suppressed for all adapted widgets via `highlight_quad -> None`).
-    fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        if Paint::serves_legacy_plain_quads(&self.inner) {
-            return Vec::new();
-        }
-        // Own prims directly (NOT `extra_quads`, which may serve the child aggregation — those
-        // children arrive once, through the recursion below).
-        let mut quads = if self.visible() { self.own_plain_quads() } else { Vec::new() };
-        // The `WidgetHost` default's highlight inclusion (secondary/hover tint excluded), live
-        // only for widgets that opt into the legacy overlay. A forwarded highlight
-        // ([`Paint::forwarded_highlight`]) is deliberately excluded: its owner's aggregation
-        // already carries it, matching the legacy container `all_quads` overrides.
-        if Paint::legacy_focus_highlight(&self.inner) {
-            if let Some(hq) = WidgetHost::highlight_quad(self, ctx) {
-                if hq.4 != crate::colors::HIGHLIGHT_SECONDARY {
-                    quads.push(hq);
-                }
-            }
-        }
-        if self.visible() {
-            // Container aggregation, replicating the shared legacy loop (Layer, Switcher):
-            // children contribute their plain quads, except a rounded-cornered child's
-            // background quad — that one arrives through `all_rounded_quads` instead.
-            for child in self.visible_children() {
-                let widget = unsafe { &*child };
-                let (wx, wy, ww, wh) = widget.rect();
-                let has_rounded = widget.corner_style().1 != (false, false, false, false);
-                for (qx, qy, qw, qh, qc) in widget.all_quads(ctx) {
-                    if has_rounded
-                        && (qx - wx).abs() < 0.1
-                        && (qy - wy).abs() < 0.1
-                        && (qw - ww).abs() < 0.1
-                        && (qh - wh).abs() < 0.1
-                    {
-                        continue;
-                    }
-                    quads.push((qx, qy, qw, qh, qc));
-                }
-            }
-        }
-        quads
-    }
 
-    fn extra_circles(&self) -> Vec<(f32, f32, f32, [f32; 4])> {
-        if !self.visible() {
-            return Vec::new();
-        }
-        self.painted_prims()
-            .into_iter()
-            .filter_map(|prim| match prim {
-                Prim::Circle { cx, cy, radius, color } => Some((cx, cy, radius, color)),
-                _ => None,
-            })
-            .collect()
-    }
 
-    fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> {
-        if !self.visible() {
-            return Vec::new();
-        }
-        self.painted_prims()
-            .into_iter()
-            .filter_map(|prim| match prim {
-                Prim::Arc { cx, cy, radius, thickness, start, end, color } => {
-                    Some((cx, cy, radius, thickness, start, end, color))
-                }
-                _ => None,
-            })
-            .collect()
-    }
 
     // --- Input concern -> `Input` ---
     /// Row-rect assignment (row-layout hosts): apply the widget's clamp
diff --git a/src/widget/owned.rs b/src/widget/owned.rs
index 630a09f..7ad0db2 100644
--- a/src/widget/owned.rs
+++ b/src/widget/owned.rs
@@ -160,15 +160,7 @@ impl<W: WidgetHost + 'static> WidgetHost for Owned<W> {
     fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { (**self).set_rect(x, y, w, h) }
     fn set_row_rect(&mut self, x: f32, w: f32) { (**self).set_row_rect(x, w) }
     fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool { (**self).hit_test(px, py, ctx) }
-    fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, f32, [f32; 4])> { (**self).highlight_quad(ctx) }
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> { (**self).extra_quads() }
-    fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> { (**self).extra_arcs() }
-    fn extra_circles(&self) -> Vec<(f32, f32, f32, [f32; 4])> { (**self).extra_circles() }
-    fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> { (**self).all_quads(ctx) }
     fn paint_self(&self, ui: &UiContext, ctx: &mut crate::scene::paint::PaintCtx) { (**self).paint_self(ui, ctx) }
-    fn all_rounded_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))> {
-        (**self).all_rounded_quads(ctx)
-    }
     fn type_name(&self) -> &'static str { (**self).type_name() }
     fn popover_rect(&self) -> Option<(f32, f32, f32, f32)> { (**self).popover_rect() }
     fn render_popover(&self, pc: &mut dyn crate::layout::RenderTarget) { (**self).render_popover(pc) }
@@ -180,7 +172,6 @@ impl<W: WidgetHost + 'static> WidgetHost for Owned<W> {
     fn visible(&self) -> bool { (**self).visible() }
     fn tick(&mut self, dt: f32, ctx: &mut UiContext) -> bool { (**self).tick(dt, ctx) }
     fn is_child_visible(&self, child_id: WidgetId) -> bool { (**self).is_child_visible(child_id) }
-    fn corner_style(&self) -> (f32, (bool, bool, bool, bool)) { (**self).corner_style() }
     fn a11y_items(&self) -> Vec<crate::a11y::A11yItem> { (**self).a11y_items() }
 }