git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit37bcc66f365877f679bf6d7874a18d5a54eed0ba
parent03dbeeb213
authorLucas Galante <[email protected]>
date2026-07-20 13:36
feat: control_relief — DE-wide default for the demo's relief styling

window_manager.control_relief (config.kdl, default on) now drives the
construction-time default for the whole relief family: raised
Button/Toggle/Dropdown plates, recessed TextBox/Slider wells, recessed
MenuBar/StatusBar bands. The per-widget with_raised / with_recessed /
with_recess builders still override in either direction, and 0 reverts
the DE to the flat look. The demo drops its per-widget opt-ins (the
defaults are now the reference), and the two tests exercising the flat
legacy bridges pin with_raised(false) / with_recess(false).

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XxjKyZK55xkRz43M8Fecsf

 src/layout.rs                    | 11 +++++++++++
 src/main.rs                      | 15 +++++++--------
 src/widget/container/menu.rs     |  2 +-
 src/widget/display/status_bar.rs |  6 ++++--
 src/widget/input/button.rs       |  6 ++++--
 src/widget/input/checkbox.rs     |  2 +-
 src/widget/input/dropdown.rs     |  2 +-
 src/widget/input/slider.rs       |  2 +-
 src/widget/input/text_box.rs     |  2 +-
 9 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/src/layout.rs b/src/layout.rs
index b1c506d..b608722 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -110,6 +110,7 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "window_manager.bevel_depth" => "bevel_depth",
                 "window_manager.bevel_width" => "bevel_width",
                 "window_manager.bevel_shader" => "bevel_shader",
+                "window_manager.control_relief" => "control_relief",
                 "window_manager.corner_shape" => "corner_shape",
                 "style.control.ramp.height" => "ramp_height",
                 "style.layout.column.gap" => "column_gap",
@@ -1526,6 +1527,16 @@ pub fn bar_wall_width() -> f32 {
     bevel_width() * 1.75
 }
 
+/// DE-wide default for the controls' relief styling (`window_manager.control_relief`
+/// in config.kdl, default on): raised Button/Toggle/Dropdown plates, recessed
+/// TextBox/Slider wells, recessed MenuBar/StatusBar bands. Widgets read this at
+/// construction; the per-widget `with_raised` / `with_recessed` / `with_recess`
+/// builders override it either way. `0` reverts the whole DE to the flat look.
+pub fn control_relief() -> bool {
+    lazy_init_style_registry();
+    get_style_registry().read().unwrap().get_float("control_relief").map(|v| v != 0.0).unwrap_or(true)
+}
+
 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 46f8fb6..efbdd65 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -141,23 +141,22 @@ impl Application for DemoApp {
     ) -> Self {
         cce_ui::scale::set_scale_factor(1.0);
         Self {
-            button: Button::new(0.0, 0.0, 0.0, 0.0).with_label("Click me").with_raised(true),
-            toggle: Toggle::new().with_raised(true),
+            // Relief styling (raised buttons/toggles/dropdowns, recessed
+            // wells) is the `control_relief` config default — no opt-in.
+            button: Button::new(0.0, 0.0, 0.0, 0.0).with_label("Click me"),
+            toggle: Toggle::new(),
             // Slider `value` is NORMALIZED 0..1; `with_range` only scales the readout
             // (`get_scaled_value`). Wheel nudging is an explicit opt-in.
             slider: Slider::new()
                 .with_range(0.0, 100.0)
                 .with_value(0.4)
-                .with_scroll(true)
-                .with_recessed(true),
+                .with_scroll(true),
             name_box: TextBox::new(String::new())
-                .with_placeholder("Type a name...")
-                .with_recessed(true),
+                .with_placeholder("Type a name..."),
             theme_dropdown: Dropdown::new(
                 vec!["Forest".into(), "Ocean".into(), "Ember".into()],
                 0,
-            )
-            .with_raised(true),
+            ),
             toggle_on: false,
             clicks: 0,
             status: "Ready.".to_string(),
diff --git a/src/widget/container/menu.rs b/src/widget/container/menu.rs
index 988707d..14e1412 100644
--- a/src/widget/container/menu.rs
+++ b/src/widget/container/menu.rs
@@ -72,7 +72,7 @@ impl MenuBar {
             curved_circle: None,
             blur: false,
             color: None,
-            recessed: false,
+            recessed: crate::layout::control_relief(),
             title: String::new(),
             menus: Adapted::new(ButtonStrip::new(x, y, w, h).with_inherit_menubar_font(true)),
             menu_items: Vec::new(),
diff --git a/src/widget/display/status_bar.rs b/src/widget/display/status_bar.rs
index 7326375..7b7f43b 100644
--- a/src/widget/display/status_bar.rs
+++ b/src/widget/display/status_bar.rs
@@ -36,7 +36,7 @@ impl StatusBar {
             text_offset_x: None,
             text_color: None,
             bg_color: None,
-            recessed: false,
+            recessed: crate::layout::control_relief(),
         })
     }
 
@@ -206,7 +206,9 @@ mod tests {
     #[test]
     fn manual_host_text_pipeline() {
         let mut fs = glyphon::FontSystem::new();
-        let mut bar = StatusBar::new().with_text("hello").with_text_offset_x(15.0);
+        // Pin the flat style: the bg-quad bridge under test is skipped by the
+        // config-default recessed band.
+        let mut bar = StatusBar::new().with_text("hello").with_text_offset_x(15.0).with_recess(false);
         WidgetHost::set_rect(&mut bar, 0.0, 570.0, 800.0, 30.0);
 
         assert!(bar.text_buf.is_none(), "no buffer before prepare_text");
diff --git a/src/widget/input/button.rs b/src/widget/input/button.rs
index e2ec697..61bcc79 100644
--- a/src/widget/input/button.rs
+++ b/src/widget/input/button.rs
@@ -66,7 +66,7 @@ impl Button {
             justify: Justification::Center,
             label: None,
             hovered: false,
-            raised: false,
+            raised: crate::layout::control_relief(),
         }
     }
 
@@ -439,7 +439,9 @@ mod tests {
     #[test]
     fn geometry_and_label_parity() {
         let ctx = UiContext::new();
-        let b = Button::new(0.0, 0.0, 100.0, 24.0).with_label("Go");
+        // Pin the flat style: this test is about the legacy quad-bridge paths,
+        // which the config-default raised plate bypasses entirely.
+        let b = Button::new(0.0, 0.0, 100.0, 24.0).with_label("Go").with_raised(false);
 
         let radius = crate::layout::button_corner_radius();
         let rounded = WidgetHost::all_rounded_quads(&b, &ctx);
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index 61d43ff..2daed8d 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -240,7 +240,7 @@ impl Toggle {
             hovered: false,
             focused: false,
             justify: Justification::Center,
-            raised: false,
+            raised: crate::layout::control_relief(),
         })
     }
 
diff --git a/src/widget/input/dropdown.rs b/src/widget/input/dropdown.rs
index d4b087b..6007e8f 100644
--- a/src/widget/input/dropdown.rs
+++ b/src/widget/input/dropdown.rs
@@ -130,7 +130,7 @@ impl Dropdown {
             label: None,
             hovered: false,
             corner_frame: None,
-            raised: false,
+            raised: crate::layout::control_relief(),
         })
     }
 
diff --git a/src/widget/input/slider.rs b/src/widget/input/slider.rs
index 5e17aff..24e721a 100644
--- a/src/widget/input/slider.rs
+++ b/src/widget/input/slider.rs
@@ -70,7 +70,7 @@ impl Slider {
             editor_state: TextEditorState::new(String::new()),
             just_changed: false,
             label: None,
-            recessed: false,
+            recessed: crate::layout::control_relief(),
         })
     }
 
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index 65b67da..aa5d8fd 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -125,7 +125,7 @@ impl TextBox {
             label: None,
             hovered: false,
             rect: Rect { x: 0.0, y: 0.0, width: 0.0, height: 0.0 },
-            recessed: false,
+            recessed: crate::layout::control_relief(),
         })
     }