git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commit92995d758e9ad24b6df9635760bd9228b46107ef
parent5e6b5b8f9c
authorLucas Galante <[email protected]>
date2026-09-03 12:27
popover layer: replay carves in the widget's own order

The page dropdown's hovered-row highlight was drawn UNDER the menu's
frosted plate: the popover collector split a widget's emission into
plain rects and inset-plate carves, and display_list replayed all the
rects and then all the carves — but a Dropdown draws its highlight after
the plate it claims for the menu face. PageContent now records, per
carve, how many rects preceded it, and display_list slots each carve
back between the rects it was claimed between. Page-widget popovers'
marks shift past the chrome popover's rects when they are merged.

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

 src/app.rs      | 10 ++++++++++
 src/main.rs     | 46 +++++++++++++++++++++++++++++++++-------------
 src/renderer.rs |  7 ++++++-
 3 files changed, 49 insertions(+), 14 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 4409aed..7a13f3b 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -170,6 +170,12 @@ pub struct PageContent {
     /// depth, carve), page coordinates, pre-scroll; display_list re-emits them
     /// as real relief prims.
     pub control_reliefs: Vec<ControlCarve>,
+    /// Per carve, `rects.len()` at the moment it was claimed — the carve's
+    /// place in the widget's own emission order. A Dropdown draws its
+    /// hovered-row highlight AFTER the inset plate it claims for the menu
+    /// face; replaying every rect and then every carve put the frosted plate
+    /// over the highlight. The popover layer interleaves on these marks.
+    pub control_relief_marks: Vec<usize>,
     pub clip_stack: Vec<[f32; 4]>,
     pub measure_only: bool,
 }
@@ -182,6 +188,7 @@ impl Default for PageContent {
             buttons: Vec::new(),
             reliefs: Vec::new(),
             control_reliefs: Vec::new(),
+            control_relief_marks: Vec::new(),
             clip_stack: Vec::new(),
             measure_only: true,
         }
@@ -196,6 +203,7 @@ impl PageContent {
             buttons: Vec::new(),
             reliefs: Vec::new(),
             control_reliefs: Vec::new(),
+            control_relief_marks: Vec::new(),
             clip_stack: Vec::new(),
             measure_only: false,
         }
@@ -366,6 +374,7 @@ impl RenderTarget for PageContent {
         if self.measure_only {
             return;
         }
+        self.control_relief_marks.push(self.rects.len());
         self.control_reliefs.push(ControlCarve::Plate { x, y, w, h, radius, depth, color });
     }
 
@@ -377,6 +386,7 @@ impl RenderTarget for PageContent {
         if self.measure_only {
             return;
         }
+        self.control_relief_marks.push(self.rects.len());
         self.control_reliefs.push(ControlCarve::Step(*carve));
     }
 
diff --git a/src/main.rs b/src/main.rs
index 77d118d..3f57922 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -47,6 +47,10 @@ struct SystemInterface {
     /// Popover-layer inset plates (window coords, scroll already applied) —
     /// the dropdown's grown-trigger surface via the inset_plate hook.
     popover_control_reliefs: Vec<ControlCarve>,
+    /// Per popover carve, the index into `popover_widgets` it precedes
+    /// (`PageContent::control_relief_marks`) — display_list slots each carve
+    /// back between the rects the widget drew before and after it.
+    popover_control_relief_marks: Vec<usize>,
     page_buttons: Vec<(cce_ui::widget::Adapted<cce_ui::widget::Button>, AppAction)>,
 
     sidebar_width: f32,
@@ -168,6 +172,7 @@ impl cce_ui::engine::Application for SystemInterface {
             popover_widgets: Vec::new(),
             popover_texts: Vec::new(),
             popover_control_reliefs: Vec::new(),
+            popover_control_relief_marks: Vec::new(),
             page_buttons: Vec::new(),
             sidebar_width,
             header_height: 0.0,
@@ -504,19 +509,13 @@ impl cce_ui::engine::Application for SystemInterface {
         if let Some((qx, qy, qw, qh, qc)) = cce_ui::widget::hover_animation::get_quad() {
             pc.quad(Rect { x: qx, y: qy - self.scroll_y, width: qw, height: qh }, qc);
         }
-        for w in &self.popover_widgets {
-            let rect = Rect { x: w.x, y: w.y, width: w.w, height: w.h };
-            if w.radius > 0.1 {
-                pc.rounded_rect(rect, w.radius, w.corners, w.color);
-            } else {
-                pc.quad(rect, w.color);
-            }
-        }
-        // Popover surfaces claimed through the inset_plate hook (the dropdown's
-        // grown-trigger plate): real relief prims at the popover layer, over
-        // the page and its control troughs.
-        for &carve in &self.popover_control_reliefs {
-            match carve {
+        // Popover rects and the surfaces claimed through the inset_plate hook
+        // (the dropdown's menu plate), replayed in the widget's OWN order:
+        // each carve goes out just before the rect it was claimed ahead of,
+        // so the hovered-row highlight a Dropdown draws after its plate lands
+        // on top of the frosted face instead of underneath it.
+        {
+            let emit_carve = |pc: &mut cce_ui::scene::paint::PaintCtx, carve: ControlCarve| match carve {
                 ControlCarve::Plate { x, y, w, h, radius, depth, color } => pc.inset_plate(
                     Rect { x, y, width: w, height: h },
                     (radius, radius, radius, radius),
@@ -524,6 +523,27 @@ impl cce_ui::engine::Application for SystemInterface {
                     depth,
                 ),
                 ControlCarve::Step(c) => pc.carve(&c),
+            };
+            let mut carves = self
+                .popover_control_reliefs
+                .iter()
+                .copied()
+                .zip(self.popover_control_relief_marks.iter().copied())
+                .peekable();
+            for (i, w) in self.popover_widgets.iter().enumerate() {
+                while carves.peek().map_or(false, |&(_, mark)| mark <= i) {
+                    let (carve, _) = carves.next().unwrap();
+                    emit_carve(&mut pc, carve);
+                }
+                let rect = Rect { x: w.x, y: w.y, width: w.w, height: w.h };
+                if w.radius > 0.1 {
+                    pc.rounded_rect(rect, w.radius, w.corners, w.color);
+                } else {
+                    pc.quad(rect, w.color);
+                }
+            }
+            for (carve, _) in carves {
+                emit_carve(&mut pc, carve);
             }
         }
         for (text, font_size, x, y, col, font, bounds) in self.texts.iter().chain(self.popover_texts.iter()) {
diff --git a/src/renderer.rs b/src/renderer.rs
index 4aa2e66..45d8064 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -656,6 +656,9 @@ impl SystemInterface {
                     }
                 }
             }
+            // The chrome popover's rects are already in place: the page
+            // carves' marks shift past them so the interleave stays true.
+            let rect_base = popover_pc.rects.len();
             for (c, x, y, w, h, r, corners) in page_pop_pc.rects {
                 popover_pc.rects.push((c, x, y - self.scroll_y, w, h, r, corners));
             }
@@ -663,8 +666,9 @@ impl SystemInterface {
                 let shifted = bounds.map(|[l, tb, rr, b]| [l, tb - self.scroll_y, rr, b - self.scroll_y]);
                 popover_pc.texts.push((t, size, x, y - self.scroll_y, tc, font, shifted));
             }
-            for carve in page_pop_pc.control_reliefs {
+            for (carve, mark) in page_pop_pc.control_reliefs.into_iter().zip(page_pop_pc.control_relief_marks) {
                 popover_pc.control_reliefs.push(carve.shifted_y(-self.scroll_y));
+                popover_pc.control_relief_marks.push(rect_base + mark);
             }
         }
         if cce_ui::widget::context_menu::is_visible() {
@@ -703,6 +707,7 @@ impl SystemInterface {
         }).collect();
         self.popover_texts = popover_pc.texts;
         self.popover_control_reliefs = popover_pc.control_reliefs;
+        self.popover_control_relief_marks = popover_pc.control_relief_marks;
 
         self.widgets = widgets;
         self.texts = texts;