cloud storage client
git clone https://git.lucas.co/cce-cloud.git
fix: keep a groove between menu rows inside the shared recess
The group well made the rows one continuous face, so the buttons stopped
being distinct. They should share a recess AND stay separate: a seam
groove at every internal boundary.
Two things this needed. The walls are drawn at HALF the well's depth so
they meet at the boundary — at full depth the seam reads as two separate
hairlines about 9px apart with flat floor between them, where the well's
own ring measures a 4px dark-to-light V. And the seams are drawn AFTER
the rows rather than before: recess() is pure shading with no fill, a
hovered row fills its entire rect, and a groove straddles the boundary
between two rows — drawn underneath, the hover erased half of every
groove it touched.
Shadow-verified: rows read as distinct compartments in one well, and
hovering the middle row fills it edge to edge with both its grooves
still carved over the highlight.
Co-Authored-By: Claude Opus 5 <[email protected]>
src/json_layout.rs | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/src/json_layout.rs b/src/json_layout.rs
index ca3d9e6..b7fcf8d 100644
--- a/src/json_layout.rs
+++ b/src/json_layout.rs
@@ -605,6 +605,7 @@ impl cce_ui::widget::Paint for JsonLayoutWidget {
// bounding box is a single continuous well rather than one per item.
let radius = cce_ui::layout::button_corner_radius();
let face = cce_ui::colors::button_background_color();
+ let mut seams: Vec<(Rect, f32)> = Vec::new();
let mut i = 0;
while i < self.widgets.len() {
let w = &self.widgets[i];
@@ -635,6 +636,24 @@ impl cce_ui::widget::Paint for JsonLayoutWidget {
pc.clip(clip, |pc| {
pc.inset_plate(run, (radius, radius, radius, radius), face, depth);
});
+ // Seams are collected, not drawn yet: they are pure shading and
+ // must land ON TOP of the rows. A hovered row fills its whole
+ // rect, and the groove straddles the boundary between two rows —
+ // drawn underneath, the hover would erase half of each groove it
+ // touches.
+ //
+ // Half depth so the two walls MEET at the boundary rather than
+ // leaving flat floor between them: at full depth the seam reads as
+ // two separate hairlines ~9px apart instead of one groove, against
+ // the well's own ring which measures a 4px dark-to-light V.
+ let seam_d = depth * 0.5;
+ for k in start..end {
+ let seam = self.widgets[k].y + self.widgets[k].h;
+ seams.push((
+ Rect { x: run.x, y: seam - seam_d, width: run.width, height: 2.0 * seam_d },
+ seam_d,
+ ));
+ }
i = end + 1;
}
@@ -661,6 +680,13 @@ impl cce_ui::widget::Paint for JsonLayoutWidget {
}
});
}
+ // Seam grooves last: pure shading, composed over the rows so a hovered
+ // row's fill cannot erase the grooves it straddles.
+ for (rect, seam_d) in seams {
+ pc.clip(clip, |pc| {
+ pc.recess_edges(rect, (0.0, 0.0, 0.0, 0.0), seam_d, (true, false, true, false));
+ });
+ }
for (tl, bounds) in self.own_labels_with_bounds(&dummy) {
pc.text_with(tl.text, tl.x, tl.y, tl.font_size, tl.color, None, bounds);
}