status bar
git clone https://git.lucas.co/cce-status-interface.git
feat: configurable bevel effect on the module boxes
New style keys, canonical at style { status ... } in config.kdl:
- box_bevel="raised"|"inset" — raised draws each module box as a lit
plate (fill + rolled lip); inset keeps the flat fill and carves a
recess rim over it. Absent/other values keep the flat boxes.
- box_bevel_depth=(f64)N — roll width of the lip in logical px
(default 3.0; the DE-wide bevel_width is window-scale and far too
fat for a bar box).
Applied in the display_list replay, so every RoundedBox gets the
treatment uniformly — the per-module boxes and the window module's
viewport tabs alike, with the same positional corner→radius mapping
the RoundedRect prim uses. Config reloads live via the existing
mtime poll in tick().
Co-Authored-By: Claude Fable 5 <[email protected]>
src/config.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++
src/main.rs | 38 ++++++++++++++++++++++++++++++++++----
2 files changed, 79 insertions(+), 4 deletions(-)
diff --git a/src/config.rs b/src/config.rs
index 6c492b4..3f319c4 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -225,6 +225,32 @@ pub(crate) fn read_status_box_corner_radius_from_config() -> f32 {
cfg_f32("/style/status/box_corner_radius", "status_box_corner_radius").unwrap_or(4.0)
}
+/// Bevel treatment for the module boxes.
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub(crate) enum StatusBoxBevel {
+ /// A lit plate lip — the box rises out of the bar.
+ Raised,
+ /// A carved recess rim — the box sinks into the bar.
+ Inset,
+}
+
+/// `style { status box_bevel="raised"|"inset" }`; absent, `"none"`, or any
+/// other value keeps the flat boxes.
+pub(crate) fn read_status_box_bevel_from_config() -> Option<StatusBoxBevel> {
+ match cfg_string("/style/status/box_bevel", "status_box_bevel")?.to_ascii_lowercase().as_str() {
+ "raised" => Some(StatusBoxBevel::Raised),
+ "inset" => Some(StatusBoxBevel::Inset),
+ _ => None,
+ }
+}
+
+/// `style { status box_bevel_depth=(f64)N }` — the roll width of the bevel lip
+/// in logical px. The DE-wide `bevel_width` (~9px) is window-scale; module
+/// boxes in a ~24px bar want a much tighter lip.
+pub(crate) fn read_status_box_bevel_depth_from_config() -> f32 {
+ cfg_f32("/style/status/box_bevel_depth", "status_box_bevel_depth").unwrap_or(3.0)
+}
+
pub(crate) fn get_ccectl_cmd() -> String {
if let Ok(home) = std::env::var("HOME") {
let path = format!("{}/.local/bin/ccectl", home);
@@ -279,6 +305,25 @@ style {
);
}
+ #[test]
+ fn test_box_bevel_config() {
+ let val = parse_kdl(
+ r##"
+style {
+ status box_bevel="raised" box_bevel_depth=(f64)2.5
+}
+"##,
+ );
+ assert_eq!(
+ val.pointer("/style/status/box_bevel").and_then(|v| v.as_str()),
+ Some("raised")
+ );
+ assert_eq!(
+ val.pointer("/style/status/box_bevel_depth").and_then(|v| v.as_f64()),
+ Some(2.5)
+ );
+ }
+
// --- json_find_key (legacy fuzzy fallback) ---
#[test]
diff --git a/src/main.rs b/src/main.rs
index 22fffaf..490e896 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -244,6 +244,8 @@ struct StatusApp {
height: u32,
needs_rebuild: bool,
current_bg_color: [f32; 4],
+ box_bevel: Option<StatusBoxBevel>,
+ box_bevel_depth: f32,
input_regions: Vec<(i32, i32, i32, i32)>,
module_bounds: Vec<ModuleBounds>,
left_modules: Vec<Box<dyn StatusModule>>,
@@ -328,6 +330,8 @@ impl StatusApp {
let box_bg_color = read_status_box_background_color_from_config();
let status_box_radius = read_status_box_corner_radius_from_config();
+ self.box_bevel = read_status_box_bevel_from_config();
+ self.box_bevel_depth = read_status_box_bevel_depth_from_config();
self.status_bar.set_rect(0.0, 0.0, self.width as f32, self.height as f32);
if self.selected_module_name.is_some() {
@@ -993,6 +997,8 @@ impl cce_ui::engine::Application for StatusApp {
height: read_status_height_from_config() as u32,
needs_rebuild: true,
current_bg_color: color::STATUS_BG,
+ box_bevel: None,
+ box_bevel_depth: 3.0,
input_regions: Vec::new(),
module_bounds: Vec::new(),
left_modules,
@@ -1157,10 +1163,34 @@ impl cce_ui::engine::Application for StatusApp {
for rb in &self.rounded_boxes {
let rect = Rect { x: rb.x, y: rb.y, width: rb.w, height: rb.h };
- if rb.radius > 0.1 {
- pc.rounded_rect(rect, rb.radius, rb.corners, rb.color);
- } else {
- pc.quad(rect, rb.color);
+ // Same positional corner→radius mapping the RoundedRect prim uses.
+ let radii = (
+ if rb.corners.0 { rb.radius } else { 0.0 },
+ if rb.corners.1 { rb.radius } else { 0.0 },
+ if rb.corners.2 { rb.radius } else { 0.0 },
+ if rb.corners.3 { rb.radius } else { 0.0 },
+ );
+ match self.box_bevel {
+ Some(StatusBoxBevel::Raised) => {
+ // A lit plate: fill + rolled lip in one prim.
+ pc.bevel(rect, radii, rb.color, self.box_bevel_depth);
+ }
+ Some(StatusBoxBevel::Inset) => {
+ // Recess shades only the rim, so keep the flat fill under it.
+ if rb.radius > 0.1 {
+ pc.rounded_rect(rect, rb.radius, rb.corners, rb.color);
+ } else {
+ pc.quad(rect, rb.color);
+ }
+ pc.recess(rect, radii, self.box_bevel_depth);
+ }
+ None => {
+ if rb.radius > 0.1 {
+ pc.rounded_rect(rect, rb.radius, rb.corners, rb.color);
+ } else {
+ pc.quad(rect, rb.color);
+ }
+ }
}
}