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

commit1d6879d1586417b52a10d8239fd65960ffdac017
parente2077669df
authorLucas Galante <[email protected]>
date2026-07-09 16:20
feat(dropdown): app-owned corner_frame — concentric cut without a Backplate ancestor

Dropdown's backplate-concentric corner adjustment walked the tracked-parent
chain for a Backplate; once an app dissolves its root Backplate (Phase 6
per-app migration) the walk finds nothing and the cut silently degrades to a
plain rounded box. set_corner_frame hands the widget the plate frame
(rect, radius, corners) explicitly and takes precedence over the walk.
First consumer: cce-system-settings' page dropdown, flush against the
window's rounded bottom-right corner (Phase 6s).

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

 src/widget/input/dropdown.rs | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/widget/input/dropdown.rs b/src/widget/input/dropdown.rs
index 59ea0e2..fa77401 100644
--- a/src/widget/input/dropdown.rs
+++ b/src/widget/input/dropdown.rs
@@ -60,6 +60,11 @@ pub struct Dropdown {
     /// bookkeeping hit-tests through [`Input::hit`], which includes the open popover — matching
     /// the legacy `on_cursor_moved` + popover-aware `hit_test` pair).
     hovered: bool,
+    /// App-owned concentric frame (Phase 6s): `(rect, radius, corners)` of the rounded plate
+    /// the dropdown sits in. When set, the corner adjustment uses it INSTEAD of walking for a
+    /// `Backplate` ancestor — the hook that keeps the adjustment after an app dissolves its
+    /// root Backplate (the walk finds nothing once the widget is parentless).
+    corner_frame: Option<((f32, f32, f32, f32), f32, (bool, bool, bool, bool))>,
 }
 
 impl Dropdown {
@@ -78,9 +83,15 @@ impl Dropdown {
             auto_width: false,
             label: None,
             hovered: false,
+            corner_frame: None,
         })
     }
 
+    /// Set (or clear) the app-owned concentric frame — see the `corner_frame` field docs.
+    pub fn set_corner_frame(&mut self, frame: Option<((f32, f32, f32, f32), f32, (bool, bool, bool, bool))>) {
+        self.corner_frame = frame;
+    }
+
     pub fn take_change(&mut self) -> bool {
         let changed = self.just_changed;
         self.just_changed = false;
@@ -234,11 +245,12 @@ impl Dropdown {
         let mut outer_radii = [radius; 4];
         let mut inner_radii = [inner_radius; 4];
 
-        if let Some(bp) = self.backplate_ancestor() {
-            let (px, py, pw, ph) = unsafe { (*bp).rect() };
-            let pr = unsafe { (*bp).corner_radius() };
-            let (pr1, pr2, pr3, pr4) = unsafe { (*bp).rounded_corners() };
-
+        let frame = self.corner_frame.or_else(|| {
+            self.backplate_ancestor().map(|bp| unsafe {
+                ((*bp).rect(), (*bp).corner_radius(), (*bp).rounded_corners())
+            })
+        });
+        if let Some(((px, py, pw, ph), pr, (pr1, pr2, pr3, pr4))) = frame {
             let g_left = x - px;
             let g_top = y - py;
             let g_right = (px + pw) - (x + w);