widget gallery and compositor test bench
refactor: each root widget is walked once, not replayed from its views
The rounded and plain geometry passes and replay_non_quad_prims are
replaced by one paint_root_into walk per root widget, its text prims
dropped (the text pass is unchanged) and the rest appended under the
exhibit clip with PaintCtx::append_items. The only change on screen is a
fix: the ButtonStrip's selected segment loses the outline an old-order
quad overlay drew over it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/main.rs | 79 +++++++++++++------------------------------------------------
1 file changed, 16 insertions(+), 63 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index e294ce2..dd83bf6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1427,56 +1427,26 @@ impl cce_ui::engine::Application for State {
pc.well_floor(well, radius, &cce_ui::scene::Material::pane(), false);
}
- let push_rounded = |pc: &mut cce_ui::scene::paint::PaintCtx, qx: f32, qy: f32, qw: f32, qh: f32, qr: f32, qc: [f32; 4], qcorners: (bool, bool, bool, bool)| {
- let rect = Rect { x: qx, y: qy, width: qw, height: qh };
- if qr > 0.1 {
- pc.rounded_rect(rect, qr, qcorners, qc);
- } else {
- pc.quad(rect, qc);
- }
- };
-
+ // ── Geometry: each root widget's paint walk, everything but its text ──
+ // A widget with a ui-tree parent (the page selector under the status bar) is
+ // painted by that parent's walk. The text goes in the text pass below, which
+ // culls it under open popovers and cuts it at the foot of the well's wall.
+ // (Until 2026-10-08 this was the legacy tuple views — every rounded quad, then
+ // every plain quad, then the other prims replayed — which put a widget's quads
+ // over everything else it painted, whatever its own order.)
for i in 0..self.roster.len() {
let w = self.roster.get_dyn(i);
- if !self.is_widget_visible(i) {
+ if !self.is_widget_visible(i) || self.ui_context.tree.parent_ptr(w.base().id()).is_some() {
continue;
}
- {
- let clip = if !self.is_child && (is_exhibit(i) || is_overlay(i)) { Some(self.exhibit_viewport()) } else { None };
- if let Some((vx, vy, vw, vh)) = clip {
- pc.push_clip(Rect { x: vx, y: vy, width: vw, height: vh });
- }
- for (qx, qy, qw, qh, qr, qc, qcorners) in w.all_rounded_quads(&self.ui_context) {
- push_rounded(&mut pc, qx, qy, qw, qh, qr, qc, qcorners);
- }
- if clip.is_some() {
- pc.pop_clip();
- }
- }
- }
-
- // ── Plain geometry ──
- for i in 0..self.roster.len() {
- let w = self.roster.get_dyn(i);
- if !self.is_widget_visible(i) {
- continue;
- }
- {
- let clip = if !self.is_child && (is_exhibit(i) || is_overlay(i)) { Some(self.exhibit_viewport()) } else { None };
- if let Some((vx, vy, vw, vh)) = clip {
- pc.push_clip(Rect { x: vx, y: vy, width: vw, height: vh });
- }
- for (qx, qy, qw, qh, qc) in w.all_quads(&self.ui_context) {
- pc.quad(Rect { x: qx, y: qy, width: qw, height: qh }, qc);
- }
- // The quad bridges above carry only quads. A widget that paints
- // borders, circles, arcs or vectors (the round Checkbox's ring and
- // dot, a slider's knob, a button's border) would lose them, so
- // replay every other own prim; text stays with the text pass.
- replay_non_quad_prims(w, &self.ui_context, &mut pc);
- if clip.is_some() {
- pc.pop_clip();
- }
+ let mut walk = cce_ui::scene::paint::PaintCtx::new();
+ cce_ui::scene::painter::paint_root_into(&self.ui_context, w, &mut walk);
+ let geometry = walk.finish().items.into_iter().filter(|it| !matches!(it.prim, Prim::Text { .. }));
+ if !self.is_child && (is_exhibit(i) || is_overlay(i)) {
+ let (vx, vy, vw, vh) = self.exhibit_viewport();
+ pc.clip(Rect { x: vx, y: vy, width: vw, height: vh }, |pc| pc.append_items(geometry));
+ } else {
+ pc.append_items(geometry);
}
}
// The well's rim over the exhibits, then the scrollbar's FORE copy over
@@ -1893,23 +1863,6 @@ impl cce_ui::engine::Application for State {
}
-/// Re-emit a widget's own prims other than quads, rounded rects and text: the
-/// gallery's paint loop bridges quads through `all_quads`/`all_rounded_quads` and
-/// text through the text pass, and would otherwise drop a widget's borders,
-/// circles, arcs and vectors.
-fn replay_non_quad_prims(w: &(dyn WidgetHost + 'static), ui: &cce_ui::context::UiContext, pc: &mut cce_ui::scene::paint::PaintCtx) {
- let mut scratch = cce_ui::scene::paint::PaintCtx::new();
- w.paint_self(ui, &mut scratch);
- for item in scratch.finish().items {
- match item.prim {
- Prim::Quad { .. } | Prim::RoundedRect { .. } | Prim::Text { .. } => {}
- prim => {
- let _ = pc.replay(prim);
- }
- }
- }
-}
-
/// A `Command` that re-runs this binary as a child window; the caller adds the flags.
fn child_command() -> Option<std::process::Command> {
let exe = std::env::current_exe().ok()?;