GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
style: rocker toggle faces carry beveled edges
The raised Toggle's two angled faces now read as true surfaces: the
state half's lip rises out of the plate (boss_edges), the other falls
away (recess_edges), hinge wall open on both so the faces meet in a
crease. Geometry lives in Toggle::rocker_reliefs — consumed by the
widget's own paint and by ParametersBg::reliefs, whose toggle branch
previously emitted nothing, so legacy-view hosts (cce-designer) lost
the relief styling entirely.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/widget/container/parameters_bg.rs | 16 ++++++++++++-
src/widget/input/checkbox.rs | 44 +++++++++++++++++++++++++++++++----
2 files changed, 55 insertions(+), 5 deletions(-)
diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index 11b250e..70afda9 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -1013,7 +1013,21 @@ impl ParametersBg {
self.buttons[i].as_ref().map(|w| (w as &dyn WidgetHost, crate::layout::button_corner_radius(), true))
} else if p.2 == "toggle" || p.2 == "checkbox" {
// The rocker's flat faces are RoundedRect prims — they arrive
- // through the rounded-quad view; no relief entries.
+ // through the rounded-quad view. The entries here are the
+ // faces' beveled edges (`Toggle::rocker_reliefs`, exactly what
+ // the widget's own raised paint emits): the state half's lip
+ // rises, the other falls away, hinge wall open on both.
+ if let Some(t) = &self.toggles[i] {
+ let (x, y, w, h) = t.rect();
+ if w > 0.0 && h > 0.0 {
+ let ty = crate::widget::label_offset(t);
+ let rect = Rect { x, y: y + ty, width: w, height: h - ty };
+ let depth = crate::layout::bevel_width().min(rect.height * 0.2);
+ for (half, radii, walls, raised) in t.inner().rocker_reliefs(rect) {
+ out.push((half.x, half.y, half.width, half.height, radii, depth, raised, walls));
+ }
+ }
+ }
None
} else {
if let Some(s) = &self.sliders[i] {
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index e0ab62c..4995e4b 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -226,9 +226,10 @@ pub struct Toggle {
/// Where the label sits across the pill. Mirrors `Button::justify` — same enum, same
/// 8px edge inset — so the two read as one control set wherever they share a column.
justify: Justification,
- /// Raised style: the pill renders as a rocker — the state half raised, the
- /// other recessed (`rocker_reliefs`) — and the flat style's state gradient
- /// is dropped.
+ /// Raised style: the pill renders as a rocker — two angled faces with
+ /// beveled edges, the state half raised, the other recessed (see
+ /// `rocker_faces` and the paint impl) — and the flat style's state
+ /// gradient is dropped.
raised: bool,
}
@@ -290,6 +291,28 @@ impl Toggle {
[(bottom, bottom_corners, LIT), (top, top_corners, SHADED)]
}
}
+
+ /// The rocker faces' beveled edges over `rect`: (half rect, per-corner
+ /// radii, (top, right, bottom, left) walls, raised). Companion to
+ /// `rocker_faces`, in the same order — the state half's lip rises out of
+ /// the surface (raised → `boss_edges`), the other falls away
+ /// (`recess_edges`). The hinge wall is open on both so the faces meet in
+ /// a crease, the recessed side reading as a step down from the raised
+ /// one rather than a double-shaded trough.
+ pub fn rocker_reliefs(
+ &self,
+ rect: Rect,
+ ) -> [(Rect, (f32, f32, f32, f32), (bool, bool, bool, bool), bool); 2] {
+ let r = crate::layout::toggle_corner_radius();
+ let mut state_half = true;
+ self.rocker_faces(rect).map(|(half, corners, _)| {
+ let is_top = corners.0;
+ let radii = if is_top { (r, r, 0.0, 0.0) } else { (0.0, 0.0, r, r) };
+ let walls = if is_top { (true, true, false, true) } else { (false, true, true, true) };
+ let raised = std::mem::take(&mut state_half);
+ (half, radii, walls, raised)
+ })
+ }
}
impl Adapted<Toggle> {
@@ -351,7 +374,12 @@ impl Paint for Toggle {
if self.raised {
// The rocker: two flat angled faces pivoting about the midline
// (see `rocker_faces`) — the physical read of the flat style's
- // state gradient, which this style drops entirely.
+ // state gradient, which this style drops entirely. Each face
+ // carries a beveled edge along its outer perimeter: the state
+ // face's lip rises (boss), the other's falls away (recess). The
+ // hinge wall is omitted on both so the faces still meet in a
+ // crease, the recessed side reading as a step down from the
+ // raised one.
if bg[3] > 0.001 {
if radius > 0.0 {
ctx.rounded_rect(rect, radius, (true, true, true, true), bg);
@@ -362,6 +390,14 @@ impl Paint for Toggle {
for (half, corners, tint) in self.rocker_faces(rect) {
ctx.rounded_rect(half, radius, corners, tint);
}
+ let depth = crate::layout::bevel_width().min(h * 0.2);
+ for (half, radii, walls, raised) in self.rocker_reliefs(rect) {
+ if raised {
+ ctx.boss_edges(half, radii, depth, walls);
+ } else {
+ ctx.recess_edges(half, radii, depth, walls);
+ }
+ }
} else if radius > 0.0 {
ctx.rounded_rect(rect, radius, (true, true, true, true), bg);
} else {