GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
feat: soften the plate-to-bar transition wall
The step where a menubar/status bar carves into the window plate read as a
crisp groove: a spec line, a below-face dip, and the bright shoulder all
alternating inside one bevel_width-wide wall. Two changes make it a broad
soft roll:
- Grouped-carve depth saturates at the nominal bevel_width in the
tessellator, so a wall wider than the plate's own perimeter roll spreads
the same step over the longer run (flatter slope) instead of cutting
proportionally deeper. Carves at or under bevel_width are bit-identical.
- layout::bar_wall_width() (1.75x bevel_width) is the shared bar wall span,
used by recessed MenuBar/StatusBar (flush single-wall cap loosened to
0.6h; trough keeps 0.4h) and the demo's header/status bands.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XxjKyZK55xkRz43M8Fecsf
src/backend/window_runner.rs | 9 +++++++--
src/layout.rs | 9 +++++++++
src/main.rs | 4 ++--
src/widget/container/menu.rs | 7 +++++--
src/widget/display/status_bar.rs | 5 +++--
5 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 7490cc0..7dde235 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -1566,10 +1566,15 @@ pub fn tessellate_display_list(
if !edges.2 { y1 += ext; }
if !edges.3 { x0 -= ext; }
let t_px = *depth * scale;
+ // Depth saturates at the DE's nominal roll width: a wall
+ // wider than the plate's own perimeter roll spreads that
+ // same step over the longer run — a softer transition —
+ // instead of cutting proportionally deeper (which would
+ // keep the wall just as steep no matter how wide it got).
+ let k_mag = RECESS_DEPTH_RATIO * t_px.min(crate::layout::bevel_width() * scale);
// Negative depth = raised (Boss); the shader's summed
// slope vectors and curvature sign follow it.
- let k_px =
- if raised { -RECESS_DEPTH_RATIO * t_px } else { RECESS_DEPTH_RATIO * t_px };
+ let k_px = if raised { -k_mag } else { k_mag };
if let Some(p) = batches[bi].plate.as_mut() {
if p.host[1] == 0.0 {
p.host[0] = features.len() as f32;
diff --git a/src/layout.rs b/src/layout.rs
index bc4a467..b1c506d 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1517,6 +1517,15 @@ pub fn bevel_width() -> f32 {
get_style_registry().read().unwrap().get_float("bevel_width").unwrap_or(9.3)
}
+/// Roll-off width for the wall where a bar (menubar / status bar / the demo's
+/// header band) steps down into the window plate. Wider than the plate's own
+/// perimeter roll on purpose: the carve depth saturates at `bevel_width` in the
+/// tessellator, so the extra width flattens the wall's slope — a soft, gradual
+/// transition into the bar — instead of cutting a proportionally deeper groove.
+pub fn bar_wall_width() -> f32 {
+ bevel_width() * 1.75
+}
+
pub fn toggle_corner_radius() -> f32 {
lazy_init_style_registry();
get_style_registry().read().unwrap().get_float("toggle_corner_radius").unwrap_or(4.0)
diff --git a/src/main.rs b/src/main.rs
index 358f377..25ae61d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -327,7 +327,7 @@ impl Application for DemoApp {
pc.recess_edges(
Rect { x: 0.0, y: 0.0, width: w, height: band_h },
(0.0, 0.0, 0.0, 0.0),
- bevel,
+ cce_ui::layout::bar_wall_width(),
(false, false, true, false),
);
@@ -342,7 +342,7 @@ impl Application for DemoApp {
pc.recess_edges(
Rect { x: 0.0, y: status_top, width: w, height: status_h },
(0.0, 0.0, 0.0, 0.0),
- bevel,
+ cce_ui::layout::bar_wall_width(),
(true, false, false, false),
);
diff --git a/src/widget/container/menu.rs b/src/widget/container/menu.rs
index 0eb3830..988707d 100644
--- a/src/widget/container/menu.rs
+++ b/src/widget/container/menu.rs
@@ -472,16 +472,19 @@ impl Paint for MenuBar {
// renderer applies `bevel_depth` itself), capped so a deep DE-wide setting can
// never swallow a short bar — the two walls would meet in the middle and the
// flat floor would vanish.
- let depth = crate::layout::bevel_width().min(rect.height * 0.4);
if rect.x <= 0.5 && rect.y <= 0.5 {
// Flush with the plate's top-left: the bar is a plateau one step down, not a
// trough, so its only wall is the one facing the content. The other three
// sides are the plate's outer edge, where the plate's own roll already lives
- // — carving there too would cut a second lip into the same pixels.
+ // — carving there too would cut a second lip into the same pixels. One wall
+ // straddling the boundary intrudes only half its width, so the cap is looser
+ // than the trough's.
+ let depth = crate::layout::bar_wall_width().min(rect.height * 0.6);
ctx.recess_edges(rect, (0.0, 0.0, 0.0, 0.0), depth, (false, false, true, false));
} else {
// Inset from the plate edge: a real trough, walled all round, its corners
// rounded by the roll itself.
+ let depth = crate::layout::bar_wall_width().min(rect.height * 0.4);
ctx.recess(rect, (depth, depth, depth, depth), depth);
}
} else {
diff --git a/src/widget/display/status_bar.rs b/src/widget/display/status_bar.rs
index d6bbf28..7326375 100644
--- a/src/widget/display/status_bar.rs
+++ b/src/widget/display/status_bar.rs
@@ -140,8 +140,9 @@ impl Paint for StatusBar {
if self.recessed {
// The recess shading is a light/shadow overlay — whatever the plate painted
// here shows through modulated, so no surface color is needed.
- // Capped against the bar's own height so a deep DE-wide roll can't swallow it.
- let depth = crate::layout::bevel_width().min(rect.height * 0.4);
+ // Capped against the bar's own height so a deep DE-wide roll can't swallow it
+ // (a single wall straddling the boundary intrudes only half its width).
+ let depth = crate::layout::bar_wall_width().min(rect.height * 0.6);
ctx.recess_edges(rect, (0.0, 0.0, 0.0, 0.0), depth, (true, false, false, false));
} else {
// Always the plain background quad — the rounded-against-parent variant required a