git.lucas.co / cce-status-interface
status bar
git clone https://git.lucas.co/cce-status-interface.git

commitdff815b1ec94f811797327c4e64debda0848354c
parent988431b046
authorLucas Galante <[email protected]>
date2026-08-29 23:28
style: the expanded menu is a flat glass sheet, its hover a pill

The droplet's dome shading follows the rounded-rect SDF gradient, and
on a box with long straight sides that field creases along the corner
diagonals. On the expanded menu this drew a blocky lit picture-frame
(shading band pinned to the collapsed pixels — a thin bright bevel
around a dead-flat interior, mitred at the corners) or envelope folds
across the body (band left to span the grown box); both were tried on
screen and read as broken lighting rather than water. The collapsed
drop never shows the creases because its silhouette is nearly all arc.

So the dome and its gleam fade out as the box grows — continuous in
the growth factor, near-identity just past the bar height, zero by
twice it, so the fade rides the expansion animation with nothing to
pop. The panel keeps the water terms that hug the silhouette and
cannot crease: the thin-edge clarity falloff, the fresnel rim crest,
the core tint, the contact shadow.

The hovered row's highlight becomes a rounded pill inset from the
panel edge, drawn post-scrim — the old full-width square-cornered rect
butting into the rounded silhouette was the last blocky element.

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

 CLAUDE.md   |  8 +++++++
 src/main.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 73 insertions(+), 10 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 52cdf25..02101d2 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -134,6 +134,14 @@ fetch_tray_menu_pages` into `MenuPage`/`MenuRow` pages riding a
 DBusMenu "clicked" via `send_tray_menu_event`). The compositor treats a status
 segment thicker than the bar as expanded: frozen slot, no size enforcement,
 raised above overlapped windows; the bar must reset its own height on close.
+In droplet style the expanded panel is a FLAT glass sheet: `spec_at_reference_height`
+fades `dome` and `gleam` to zero (continuously in the growth factor, gone by
+twice the bar height) because the SDF-gradient dome creases on a long-sided
+box — full strength drew a blocky lit picture-frame with the band pinned, and
+envelope folds across the body with the band grown; both were tried. The panel
+keeps the silhouette-hugging water terms (clarity, rim crest, core, contact
+shadow), and the hovered row's highlight is a rounded pill inset from the
+panel edge (`menu_hover_rect`, drawn post-scrim), not a full-width rect.
 
 **No cce-cloud popups remain in this app**: the window picker (window-module
 click → `MenuReady` rows of `Ccectl(["focus-window", id])`) is an in-surface
diff --git a/src/main.rs b/src/main.rs
index 420539d..ab3bd3b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -201,8 +201,24 @@ fn spec_at_reference_height(
     out.blend *= k;
     out.sheet_r *= k;
     out.attach *= k;
-    out.band *= k;
     out.bow *= k;
+    out.band *= k;
+    // The dome and its gleam FADE OUT as the box grows past the bar strip:
+    // dome shading follows the rounded-rect SDF gradient, and on a box with
+    // long straight sides that field creases along the corner diagonals —
+    // full strength draws a blocky lit picture-frame (band pinned) or
+    // envelope folds across the body (band grown); both were tried and read
+    // as broken lighting rather than water. A tall panel is not a bead: the
+    // expanded menu settles into a flat glass sheet that keeps the drop's
+    // OTHER water terms — the thin-edge clarity falloff, the fresnel rim
+    // crest along the lower arc, the core tint, the contact shadow — which
+    // are all silhouette-hugging and crease-free. The ramp is continuous in
+    // k — full lighting collapsed, gone once the box passes twice the bar
+    // height — so the fade rides the expansion animation with nothing to
+    // pop, and a real menu (k ≈ 0.1–0.2) lands at exactly zero.
+    let lit = ((k - 0.5) * 2.0).clamp(0.0, 1.0);
+    out.dome *= lit;
+    out.gleam *= lit;
     out
 }
 
@@ -480,6 +496,10 @@ struct StatusApp {
     /// The collapsed bubble actually drawn this rebuild (x, w), slot coords —
     /// what the in-surface menu expansion grows out of and contracts back to.
     collapsed_box: Option<(f32, f32)>,
+    /// The hovered menu row's highlight pill (x, y, w, h), set by the menu
+    /// branch of `rebuild_layout` and drawn in `display_list` AFTER the scrim
+    /// (so the pool does not darken it) and before the text.
+    menu_hover_rect: Option<(f32, f32, f32, f32)>,
     input_regions: Vec<(i32, i32, i32, i32)>,
     module_bounds: Vec<ModuleBounds>,
     left_modules: Vec<Box<dyn StatusModule>>,
@@ -583,6 +603,7 @@ impl StatusApp {
         self.module_bounds.clear();
         self.tray_item_bounds.clear();
         self.collapsed_box = None;
+        self.menu_hover_rect = None;
 
         let left_modules = std::mem::take(&mut self.left_modules);
         let right_modules = std::mem::take(&mut self.right_modules);
@@ -1032,13 +1053,13 @@ impl StatusApp {
                             });
                         } else {
                             if hovered == Some(i) && row.enabled {
-                                self.rects.push(RectWidget {
-                                    x: anim_x + 2.0,
-                                    y: iy,
-                                    w: anim_w - 4.0,
-                                    h: h,
-                                    color: [0.23, 0.35, 0.50, 0.55],
-                                });
+                                // A rounded pill inset from the panel edge,
+                                // drawn post-scrim in display_list — a
+                                // square-cornered full-width rect butting
+                                // into the rounded silhouette was the last
+                                // blocky element of the expanded menu.
+                                self.menu_hover_rect =
+                                    Some((anim_x + 6.0, iy + 1.0, anim_w - 12.0, h - 2.0));
                             }
                             self.text_prims.push((
                                 row.label.clone(),
@@ -1429,6 +1450,7 @@ impl cce_ui::engine::Application for StatusApp {
             context_menu: None,
             menu_anim: 0.0,
             menu_closing: false,
+            menu_hover_rect: None,
             bubble_w_now: 0.0,
             bubble_w_target: 0.0,
             collapsed_box: None,
@@ -1820,6 +1842,17 @@ impl cce_ui::engine::Application for StatusApp {
             pc.quad(Rect { x: r.x, y: r.y, width: r.w, height: r.h }, r.color);
         }
 
+        // The hovered menu row's pill: post-scrim so the pool cannot darken
+        // it, pre-text so the label sits on it.
+        if let Some((hx, hy, hw, hh)) = self.menu_hover_rect {
+            pc.rounded_rect(
+                Rect { x: hx, y: hy, width: hw, height: hh },
+                (hh / 2.0).min(8.0),
+                (true, true, true, true),
+                [0.23, 0.35, 0.50, 0.55],
+            );
+        }
+
         for (text, tsize, x, y, color, font, bounds, layout, _run_w) in &self.text_prims {
             match layout {
                 Some(l) => pc.text_boxed(text.clone(), *x, *y, *tsize, *color, font.clone(), *bounds, cce_ui::scene::paint::TextAttrs::default(), *l),
@@ -2449,8 +2482,6 @@ mod tests {
         assert_eq!(grown.curve, spec.curve);
         assert_eq!(grown.core, spec.core);
         assert_eq!(grown.clarity, spec.clarity);
-        assert_eq!(grown.dome, spec.dome);
-        assert_eq!(grown.gleam, spec.gleam);
         assert_eq!(grown.shine, spec.shine);
         assert_eq!(grown.rim, spec.rim);
         assert_eq!(grown.shadow, spec.shadow);
@@ -2459,6 +2490,30 @@ mod tests {
         assert!(grown.sheet_r < spec.sheet_r);
     }
 
+    #[test]
+    fn the_expanded_panel_is_a_flat_sheet_with_the_water_terms_kept() {
+        // The dome and its gleam fade OUT as the box grows: at full strength
+        // the SDF-gradient dome draws a blocky lit frame (band pinned) or
+        // envelope folds (band grown) on a long-sided panel — both tried,
+        // both read as broken lighting. A real menu (k well under 0.5) lands
+        // at exactly zero: flat glass, keeping clarity, rim, core, shadow.
+        let spec = cce_ui::scene::paint::DropletSpec::default();
+        let grown = spec_at_reference_height(spec, COLLAPSED.1, PICKER.1);
+        assert_eq!(grown.dome, 0.0);
+        assert_eq!(grown.gleam, 0.0);
+        assert_eq!(grown.clarity, spec.clarity);
+        assert_eq!(grown.rim, spec.rim);
+
+        // The fade is CONTINUOUS in the growth factor — the expansion
+        // animation passes through every k on its way down, so a step
+        // anywhere would pop mid-flight. Just past the reference it is
+        // near-identity; by twice the reference it has reached zero.
+        let barely = spec_at_reference_height(spec, COLLAPSED.1, COLLAPSED.1 + 0.5);
+        assert!((barely.dome - spec.dome).abs() < 0.1);
+        let doubled = spec_at_reference_height(spec, COLLAPSED.1, COLLAPSED.1 * 2.0);
+        assert_eq!(doubled.dome, 0.0);
+    }
+
     #[test]
     fn the_scrim_is_constant_without_the_adaptive_knob() {
         // text_contrast off leaves `demand` at zero, and the scrim is then