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

commit0dee11e77d9d011702ff8cb71b29d38b89305b73
parent8e5331d930
authorLucas Galante <[email protected]>
date2026-09-18 15:25
fix: the row menu wears the menu font, not the default sans

The file-manager's own context menu drew its labels at a hardcoded
12.0 with no family and sized its plate from `bytes * 7.5` — the same
three guesses the toolkit menu had. Over a Berkeley Mono list it read
in the default sans.

Labels and plate width now come from the menu font that
`cce_ui::widget::context_menu::label_font()` resolves, measured rather
than estimated, and the toolkit's own menu goes through
`paint_with_labels` so its family is carried too instead of dropped
with a `None`.

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

 src/main.rs | 53 ++++++++++++++++++++++++-----------------------------
 1 file changed, 24 insertions(+), 29 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index ee8d1c0..9eb671c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,12 +17,20 @@ const ROW_H: f32 = 24.0;        // context-menu / breadcrumb row height
 const DIALOG_W: f32 = 400.0;
 const DIALOG_H: f32 = 160.0;
 const MENU_MIN_W: f32 = 120.0;
-const MENU_CHAR_W: f32 = 7.5;   // approximate glyph advance used for menu sizing
 
 /// Context-menu width/height for a given set of options.
+///
+/// The width comes from the SHAPED widest label in the menu font — the same
+/// face [`cce_ui::widget::context_menu`] uses for the toolkit's own menus.
+/// The old `bytes * 7.5` estimate belonged to neither face, so the plate was
+/// sized for a font nothing here draws in.
 fn context_menu_size(options: &[(String, Option<Message>)]) -> (f32, f32) {
-    let max_len = options.iter().map(|(s, _)| s.len()).max().unwrap_or(0);
-    let w = ((max_len as f32 * MENU_CHAR_W) + 24.0).max(MENU_MIN_W);
+    let (family, size) = cce_ui::widget::context_menu::label_font();
+    let widest = options
+        .iter()
+        .map(|(s, _)| cce_ui::widget::display::measure_text_width(s, &family, size))
+        .fold(0.0f32, f32::max);
+    let w = (widest + 24.0).max(MENU_MIN_W);
     let h = options.len() as f32 * ROW_H;
     (w, h)
 }
@@ -820,9 +828,13 @@ impl FilesystemApp {
                     (false, false, last, last),
                 );
             }
-            // Text options
+            // Text options, in the DE's menu font — family and size. Drawn
+            // with a hardcoded 12.0 and no family, the row menu read in the
+            // default sans while the list it opened over wore the configured
+            // face.
+            let (menu_family, menu_size) = cce_ui::widget::context_menu::label_font();
             for (idx, (opt, _)) in self.context_menu.options.iter().enumerate() {
-                let iy = cy + idx as f32 * ROW_H + (ROW_H - 12.0) / 2.0;
+                let iy = cy + idx as f32 * ROW_H + (ROW_H - menu_size) / 2.0;
                 // The toolkit's semantic text colors, not the hand-mixed greys
                 // that came with the near-black popover face: the header's old
                 // 0.44 grey was a step above black and all but vanishes on the
@@ -834,7 +846,7 @@ impl FilesystemApp {
                 } else {
                     cce_ui::color::TEXT_FG
                 };
-                context_menu_pc.text(opt, cx + 8.0, iy, 12.0, text_color);
+                context_menu_pc.text_with_font(opt, cx + 8.0, iy, menu_size, text_color, &menu_family);
             }
         }
 
@@ -1703,29 +1715,12 @@ impl Application for FilesystemApp {
         }
 
         // while page text beneath stays clamped.
-        if cce_ui::widget::context_menu::is_visible() {
-            let menu_bounds = Some([
-                cce_ui::widget::context_menu::x(),
-                cce_ui::widget::context_menu::y(),
-                cce_ui::widget::context_menu::x() + cce_ui::widget::context_menu::w(),
-                cce_ui::widget::context_menu::y() + cce_ui::widget::context_menu::h(),
-            ]);
-            // The shared menu paints itself as a lit plate (cce-ui), so the
-            // breadcrumb's copy-path menu is the same frosted glass as the row
-            // menu above rather than the flat quads it used to stack.
-            cce_ui::widget::context_menu::paint(&mut pc);
-            for label in cce_ui::widget::context_menu::text_labels() {
-                pc.text_with(
-                    label.text.clone(),
-                    label.x,
-                    label.y,
-                    label.font_size,
-                    label.color,
-                    None,
-                    menu_bounds,
-                );
-            }
-        }
+        // The shared menu paints itself as a lit plate (cce-ui), so the
+        // breadcrumb's copy-path menu is the same frosted glass as the row
+        // menu above rather than the flat quads it used to stack. Labels come
+        // with it: `paint_with_labels` carries the menu font's family, which
+        // the hand-rolled `paint` + `text_labels()` loop here did not.
+        cce_ui::widget::context_menu::paint_with_labels(&mut pc);
         Some(pc.finish())
     }