git.lucas.co / cce-files
file manager
git clone https://git.lucas.co/cce-files.git

commitae563f0b78724eda7cb45957007dc020e22d46da
parent8307815d5c
authorLucas Galante <[email protected]>
date2026-08-06 13:02
feat: mirror the breadcrumb's per-segment button plates in the flat view

PageContent reliefs carry a kind (recessed/raised/inset) instead of a
bool; relief_inset maps to the flush inset button treatment
(WidgetFx::Inset). Browse and Network carve one inset per segment from
Breadcrumb::segment_boxes, replacing the single recessed well.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/main.rs          |  8 ++++++--
 src/pages/browse.rs  | 13 ++++++++++---
 src/pages/mod.rs     | 25 ++++++++++++++++++++-----
 src/pages/network.rs | 13 ++++++++++---
 4 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 065918e..7c62260 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -715,7 +715,7 @@ impl FilesystemApp {
                     fx: WidgetFx::Image { id: *id, alpha: *alpha },
                 });
             }
-            for (rx, ry, rw, rh, rr, rd, raised) in &pc_part.reliefs {
+            for (rx, ry, rw, rh, rr, rd, kind) in &pc_part.reliefs {
                 let (mut wy, mut wh) = (*ry, *rh);
                 if is_page_content {
                     match clip_to_viewport(wy, wh, content_y, content_y + content_h) {
@@ -731,7 +731,11 @@ impl FilesystemApp {
                     color: [0.0; 4],
                     radius: *rr,
                     corners: (true, true, true, true),
-                    fx: if *raised { WidgetFx::Boss(*rd) } else { WidgetFx::Recess(*rd) },
+                    fx: match *kind {
+                        pages::RELIEF_RAISED => WidgetFx::Boss(*rd),
+                        pages::RELIEF_INSET => WidgetFx::Inset(*rd),
+                        _ => WidgetFx::Recess(*rd),
+                    },
                 });
             }
             for (btn, action) in &pc_part.buttons {
diff --git a/src/pages/browse.rs b/src/pages/browse.rs
index 7677982..974b788 100644
--- a/src/pages/browse.rs
+++ b/src/pages/browse.rs
@@ -198,9 +198,16 @@ pub fn view(state: &mut BrowseState, view_dropdown: &mut cce_ui::widget::Adapted
     let breadcrumb_w = client_w - dropdown_w - gap;
     let (bx, by, bw, bh) = layout.allocate(breadcrumb_w, breadcrumb_h);
     cce_ui::layout::render_widget(&mut pc, &mut state.breadcrumb, bx, by, bw, bh, ctx);
-    // The breadcrumb's recessed well lives in its modern paint(); the flat view
-    // this host renders through loses it, so carve it here.
-    pc.relief_recessed(bx, by, bw, bh, cce_ui::layout::breadcrumb_corner_radius());
+    // The breadcrumb's per-segment button plates live in its modern paint();
+    // the flat view this host renders through loses them, so carve each
+    // segment's flush inset here (the dropdown-mirror idiom).
+    {
+        let r = cce_ui::layout::breadcrumb_corner_radius().min(bh * 0.5);
+        let rect = cce_ui::scene::layout::Rect { x: bx, y: by, width: bw, height: bh };
+        for (sx, sy, sw, sh) in state.breadcrumb.segment_boxes(rect) {
+            pc.relief_inset(sx, sy, sw, sh, r);
+        }
+    }
     cce_ui::layout::render_widget(&mut pc, view_dropdown, bx + bw + gap, by, dropdown_w, breadcrumb_h, ctx);
 
     // 2. Allocate and render List (ScrollBox)
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index feb45cc..b65ccdc 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -31,14 +31,19 @@ impl Page {
     }
 }
 
+pub const RELIEF_RECESSED: u8 = 0;
+pub const RELIEF_RAISED: u8 = 1;
+pub const RELIEF_INSET: u8 = 2;
+
 pub struct PageContent {
     pub rects: Vec<([f32; 4], f32, f32, f32, f32, f32, (bool, bool, bool, bool))>,
     pub texts: Vec<(String, f32, f32, f32, [f32; 4], Option<String>, Option<[f32; 4]>)>,
     pub buttons: Vec<(cce_ui::widget::Adapted<cce_ui::widget::Button>, crate::Message)>,
     /// Relief steps for the control_relief styling — (x, y, w, h, radius, depth,
-    /// raised). The flat rects own the faces; these are the edges-only boss/recess
-    /// walls emitted over them (the ParametersBg::reliefs idiom for flat-view hosts).
-    pub reliefs: Vec<(f32, f32, f32, f32, f32, f32, bool)>,
+    /// kind: [`RELIEF_RECESSED`]/[`RELIEF_RAISED`]/[`RELIEF_INSET`]). The flat rects
+    /// own the faces; these are the edges-only walls emitted over them (the
+    /// ParametersBg::reliefs idiom for flat-view hosts).
+    pub reliefs: Vec<(f32, f32, f32, f32, f32, f32, u8)>,
     /// GPU-textured quads — (image id from `cce_ui::vk::upload_rgba`, x, y, w, h,
     /// alpha). Drawn after the part's rects, so a fill emitted earlier is the floor
     /// beneath the image and overlay parts still cover it.
@@ -70,7 +75,7 @@ impl PageContent {
     pub fn relief_recessed(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32) {
         if cce_ui::layout::control_relief() {
             let depth = cce_ui::layout::bevel_width().min(h * 0.2);
-            self.reliefs.push((x, y, w, h, radius, depth, false));
+            self.reliefs.push((x, y, w, h, radius, depth, RELIEF_RECESSED));
         }
     }
 
@@ -79,7 +84,17 @@ impl PageContent {
     pub fn relief_raised(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32) {
         if cce_ui::layout::control_relief() {
             let depth = cce_ui::layout::bevel_width().min(h * 0.2);
-            self.reliefs.push((x, y, w, h, radius, depth, true));
+            self.reliefs.push((x, y, w, h, radius, depth, RELIEF_RAISED));
+        }
+    }
+
+    /// A flush inset button plate (groove ring + beveled lip, face level with the
+    /// surface — the Button treatment) over the control at (x, y, w, h) — no-op
+    /// when the DE's control_relief styling is off.
+    pub fn relief_inset(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32) {
+        if cce_ui::layout::control_relief() {
+            let depth = cce_ui::layout::bevel_width().min(h * 0.2);
+            self.reliefs.push((x, y, w, h, radius, depth, RELIEF_INSET));
         }
     }
 
diff --git a/src/pages/network.rs b/src/pages/network.rs
index 20bd733..e94eabd 100644
--- a/src/pages/network.rs
+++ b/src/pages/network.rs
@@ -115,9 +115,16 @@ pub fn view(state: &mut NetworkState, browse: &BrowseState, view_dropdown: &mut
     let dropdown_w = 120.0;
     let breadcrumb_w = cw - 16.0 - dropdown_w - 12.0;
     cce_ui::layout::render_widget(&mut pc, &mut state.breadcrumb, cx + 4.0, cy + 6.0, breadcrumb_w, 24.0, ctx);
-    // The breadcrumb's recessed well lives in its modern paint(); the flat view
-    // this host renders through loses it, so carve it here.
-    pc.relief_recessed(cx + 4.0, cy + 6.0, breadcrumb_w, 24.0, cce_ui::layout::breadcrumb_corner_radius());
+    // The breadcrumb's per-segment button plates live in its modern paint();
+    // the flat view this host renders through loses them, so carve each
+    // segment's flush inset here (the dropdown-mirror idiom).
+    {
+        let r = cce_ui::layout::breadcrumb_corner_radius().min(12.0);
+        let rect = cce_ui::scene::layout::Rect { x: cx + 4.0, y: cy + 6.0, width: breadcrumb_w, height: 24.0 };
+        for (sx, sy, sw, sh) in state.breadcrumb.segment_boxes(rect) {
+            pc.relief_inset(sx, sy, sw, sh, r);
+        }
+    }
     cce_ui::layout::render_widget(&mut pc, view_dropdown, cx + 4.0 + breadcrumb_w + 12.0, cy + 6.0, dropdown_w, 24.0, ctx);
 
     // Check if directory changed, or if last_dir is empty, and repopulate