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

commitabf27c32302ed2614cde2b27f2423fc8b54ea271
parent25989d64af
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-02 08:06
feat: breadcrumbs, the font selector and menubar triggers wear the run's edge

The last widgets drawing a trough for their flush edge: a breadcrumb's
flush run, the font selector's field and a menubar's dropdown-look
triggers now draw a field that is all run, as dropdowns and buttons do.

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

 CLAUDE.md                          | 10 +++++++---
 src/widget/container/breadcrumb.rs | 34 +++++++++++++++++++++++++++++++---
 src/widget/container/menu.rs       |  6 +++---
 src/widget/input/font_selector.rs  |  9 ++++++---
 4 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 5199f25..3423e65 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -350,9 +350,13 @@ answers with its inset plate), and the pane's dropdown rows, now in `fields` (th
 `troughs()` list went: nothing in the pane draws a trough any more). **So does every
 button's flush plate** (`Button::plate` sets the run edge; the pane's button rows are
 fields too, where they had been a raised boss the button itself never drew). So every
-flush control in a parameter pane has one edge. Breadcrumbs, the font selector and the
-other `inset_plate` callers still wear the trough. `a_dropdown_trigger_wears_the_runs_edge`
-and `focus_lights_the_plate_rim` are the tests.
+flush control in a parameter pane has one edge. **And so do the rest** (2026-10-02): a
+breadcrumb's flush run, the font selector's field and a menubar's dropdown-look triggers
+(`menu.rs`, `flush_run` in place of `inset_plate`). No widget draws a trough for its
+own edge now; `PaintCtx::inset_plate` and `Prim::Trough` remain for callers outside the
+toolkit's widgets and for `cce-relief`'s preview. `a_dropdown_trigger_wears_the_runs_edge`,
+`focus_lights_the_plate_rim` and `a_breadcrumb_and_a_font_selector_wear_the_runs_edge` are
+the tests.
 
 The pieces that feed it: `ParametersBg::fields` (the pane's list, drawn after its troughs,
 hover-tinted like them; textpick rows and spinboxes with a run are in neither `reliefs`
diff --git a/src/widget/container/breadcrumb.rs b/src/widget/container/breadcrumb.rs
index f3e2c45..37a52ac 100644
--- a/src/widget/container/breadcrumb.rs
+++ b/src/widget/container/breadcrumb.rs
@@ -52,8 +52,9 @@ pub struct Breadcrumb {
     pub right_clicked_seg: Option<usize>,
     pub network_opacity: f32,
     /// Relief stance of the segment run. `false` (the default) is the
-    /// dropdown-mirror trough: the run sits flush, sunk into the surface
-    /// behind a valley seam. `true` swaps the trough for a boss — the same
+    /// dropdown's flush plate: the run's face level with the surface, its
+    /// edge a field run's (`ControlPlate::with_run_edge`), as the dropdown
+    /// trigger and the buttons wear. `true` swaps it for a boss — the same
     /// silhouette raised out of the surface, for hosts whose breadcrumb
     /// floats in front of its plate (the designer's network editor) rather
     /// than sitting inset into a toolbar. Flat (non-relief) styling and the
@@ -360,7 +361,8 @@ impl Paint for Breadcrumb {
                 ctx.control_plate(
                     &crate::widget::ControlPlate::control(run_rect, r, stance, face)
                         .with_depth(depth)
-                        .with_tint(self.focused.then(crate::widget::ControlPlate::focus_tint)),
+                        .with_tint(self.focused.then(crate::widget::ControlPlate::focus_tint))
+                        .with_run_edge(stance == crate::widget::PlateStance::Flush),
                 );
                 for (a, b) in self.seams(rect) {
                     ctx.groove(a, b, Self::SEAM_WIDTH, depth, run_rect);
@@ -608,6 +610,32 @@ mod tests {
         s.x + s.w / 2.0
     }
 
+    /// The flush controls that are not dropdowns or buttons wear their edge
+    /// too: a breadcrumb's flush run and a font selector's field draw a
+    /// field that is all run (`ControlPlate::with_run_edge`), never a trough.
+    #[test]
+    fn a_breadcrumb_and_a_font_selector_wear_the_runs_edge() {
+        use crate::scene::paint::{PaintCtx, Prim};
+        if !crate::layout::control_relief() {
+            return;
+        }
+        let all_run = |items: Vec<crate::scene::paint::PaintItem>| {
+            assert!(!items.iter().any(|i| matches!(i.prim, Prim::Trough { .. })), "no trough");
+            items.iter().any(|i| matches!(i.prim, Prim::Field { rect, split, .. } if split < rect.x - 100.0))
+        };
+        let rect = Rect { x: 10.0, y: 20.0, width: 300.0, height: 24.0 };
+        let mut b = Breadcrumb::new();
+        b.set_path(&["home".to_string(), "lsgalante".to_string()]);
+        b.set_rect(rect.x, rect.y, rect.width, rect.height);
+        let mut pc = PaintCtx::new();
+        Paint::paint(b.inner(), rect, &mut pc);
+        assert!(all_run(pc.finish().items), "a flush breadcrumb run");
+        let f = crate::widget::input::FontSelector::new("Sans".to_string());
+        let mut pc = PaintCtx::new();
+        Paint::paint(f.inner(), rect, &mut pc);
+        assert!(all_run(pc.finish().items), "a font selector");
+    }
+
     #[test]
     fn test_breadcrumb_clicks() {
         let mut breadcrumb = Breadcrumb::new();
diff --git a/src/widget/container/menu.rs b/src/widget/container/menu.rs
index 2934b7f..1e544e0 100644
--- a/src/widget/container/menu.rs
+++ b/src/widget/container/menu.rs
@@ -505,8 +505,8 @@ impl Paint for MenuBar {
 
         // Dropdown-trigger chrome shared by the context title and the menu
         // buttons on horizontal bars: the DE-wide closed-dropdown look — a
-        // flush inset trough with a transparent face (Dropdown::
-        // paint_background's raised path) carved on a band-inset rect, with
+        // flush plate with a field run's edge and a transparent face
+        // (Dropdown::paint_background's raised path) on a band-inset rect, with
         // the state fill rounded to sit inside it. The vertical and curved
         // modes keep their plain quads — their geometry is exotic and gets no
         // trough.
@@ -517,7 +517,7 @@ impl Paint for MenuBar {
             let radius = crate::layout::dropdown_corner_radius();
             let depth = crate::layout::bevel_width().min(trough_h * 0.2);
             let (trough, radii) = crate::layout::carve_inside(trough, (radius, radius, radius, radius), depth);
-            ctx.inset_plate(trough, radii, None, depth);
+            ctx.flush_run(trough, radii, None, depth, None);
             if let Some(c) = fill {
                 ctx.rounded_rect(trough, radius, (true, true, true, true), c);
             }
diff --git a/src/widget/input/font_selector.rs b/src/widget/input/font_selector.rs
index b5d2d16..c3621cb 100644
--- a/src/widget/input/font_selector.rs
+++ b/src/widget/input/font_selector.rs
@@ -16,8 +16,8 @@ pub struct FontSelector {
     pressed: bool,
     hovered: bool,
     child: Arc<Mutex<Option<std::process::Child>>>,
-    /// Raised style, the closed Dropdown's: the field is a flush inset trough
-    /// with a transparent face (the plate shows through), the hover and press
+    /// Raised style, the closed Dropdown's: the field is a flush plate with a
+    /// field run's edge (`ControlPlate::with_run_edge`) and a transparent face (the plate shows through), the hover and press
     /// states a wash inside it. Defaults to `control_relief()`; the flat style
     /// keeps the framed dark field.
     raised: Option<bool>,
@@ -172,7 +172,10 @@ impl Paint for FontSelector {
             // transparent face, the state fill rounded to sit inside it.
             ctx.control_plate(
                 &crate::widget::ControlPlate::control(rect, r, crate::widget::PlateStance::Flush, None)
-                    .with_tint(self.focused.then(crate::widget::ControlPlate::focus_tint)),
+                    .with_tint(self.focused.then(crate::widget::ControlPlate::focus_tint))
+                    // The edge every flush control wears: a field run's, as
+                    // the dropdown trigger and the buttons do.
+                    .with_run_edge(true),
             );
             let wash = if self.pressed {
                 Some(colors::button_press_color())