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

commitd83d94bb30a7ea54f5e53defcc7fca1059442567
parentb847638d8c
authorLucas Galante <[email protected]>
date2026-07-05 12:55
feat: add config.kdl keys style.control.control_panel.{color,border_color,padding,gap} for cce-ui

 src/color.rs                          | 30 ++++++++++++++
 src/layout.rs                         | 78 +++++++++++++++++++++++++++++++++++
 src/widget/container/control_panel.rs |  8 ++--
 3 files changed, 112 insertions(+), 4 deletions(-)

diff --git a/src/color.rs b/src/color.rs
index 77caf85..f529593 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -65,6 +65,8 @@ static BACKPLATE_STATUSBAR_BLUR: RwLock<bool> = RwLock::new(false);
 static BUTTON_BACKGROUND_COLOR: RwLock<[f32; 4]> = RwLock::new(BUTTON_IDLE);
 static RAMP_BACKGROUND_COLOR: RwLock<[f32; 4]> = RwLock::new([0.08, 0.08, 0.12, 1.0]);
 static RAMP_BORDER_COLOR: RwLock<[f32; 4]> = RwLock::new([0.18, 0.18, 0.24, 1.0]);
+static CONTROL_PANEL_COLOR: RwLock<[f32; 4]> = RwLock::new([0.075, 0.082, 0.11, 1.0]); // default #13151cff
+static CONTROL_PANEL_BORDER_COLOR: RwLock<[f32; 4]> = RwLock::new([0.161, 0.173, 0.216, 1.0]); // default #292c37ff
 
 static TREE_BACKGROUND_COLOR: RwLock<[f32; 4]> = RwLock::new([0.08, 0.08, 0.12, 0.3]);
 static TREE_BORDER_COLOR: RwLock<[f32; 4]> = RwLock::new([0.18, 0.18, 0.24, 1.0]);
@@ -333,6 +335,12 @@ fn parse_and_set_colors(content: &str) {
     if let Some(c) = get_color("/style/control/ramp/border_color") {
         if let Ok(mut lock) = RAMP_BORDER_COLOR.write() { *lock = c; }
     }
+    if let Some(c) = get_color("/style/control/control_panel/color") {
+        if let Ok(mut lock) = CONTROL_PANEL_COLOR.write() { *lock = c; }
+    }
+    if let Some(c) = get_color("/style/control/control_panel/border_color") {
+        if let Ok(mut lock) = CONTROL_PANEL_BORDER_COLOR.write() { *lock = c; }
+    }
     let parsed_list_bg = get_color("/layout/list_bg_color");
     let parsed_breadcrumb_bg = get_color("/layout/breadcrumb_bg_color");
     let parsed_popover_bg = get_color("/layout/popover_bg_color");
@@ -991,6 +999,28 @@ pub fn ramp_border_color() -> [f32; 4] {
     *RAMP_BORDER_COLOR.read().unwrap()
 }
 
+pub fn control_panel_color() -> [f32; 4] {
+    load_colors_once();
+    *CONTROL_PANEL_COLOR.read().unwrap()
+}
+
+pub fn set_control_panel_color(color: [f32; 4]) {
+    if let Ok(mut lock) = CONTROL_PANEL_COLOR.write() {
+        *lock = color;
+    }
+}
+
+pub fn control_panel_border_color() -> [f32; 4] {
+    load_colors_once();
+    *CONTROL_PANEL_BORDER_COLOR.read().unwrap()
+}
+
+pub fn set_control_panel_border_color(color: [f32; 4]) {
+    if let Ok(mut lock) = CONTROL_PANEL_BORDER_COLOR.write() {
+        *lock = color;
+    }
+}
+
 pub fn set_backplate_corner_radius(radius: f32) {
     if let Ok(mut lock) = BACKPLATE_CORNER_RADIUS.write() {
         *lock = radius;
diff --git a/src/layout.rs b/src/layout.rs
index 09528a7..ca5b5f6 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -109,6 +109,8 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "window_manager.bevel_depth" => "bevel_depth",
                 "style.control.ramp.height" => "ramp_height",
                 "style.layout.column.gap" => "column_gap",
+                "style.control.control_panel.padding" => "control_panel_padding",
+                "style.control.control_panel.gap" => "control_panel_gap",
                 "style.status.normal_color" => "status_normal_color",
                 "style.status.background_color" => "status_background_color",
                 "style.status.background_blur" => "status_background_blur",
@@ -253,6 +255,8 @@ static RAMP_HEIGHT: RwLock<f32> = RwLock::new(32.0);
 static BUTTON_STRIP_SPACING: RwLock<f32> = RwLock::new(8.0);
 static SCROLLBAR_WIDTH: RwLock<f32> = RwLock::new(4.0);
 static COLUMN_GAP: RwLock<f32> = RwLock::new(16.0);
+static CONTROL_PANEL_PADDING: RwLock<f32> = RwLock::new(16.0);
+static CONTROL_PANEL_GAP: RwLock<f32> = RwLock::new(12.0);
 static TREE_OPACITY: RwLock<f32> = RwLock::new(1.0);
 static TREE_BLUR: RwLock<f32> = RwLock::new(0.0);
 
@@ -430,6 +434,24 @@ pub fn reload_config() {
                     }
                 }
             }
+            if let Some(rest) = trimmed.strip_prefix("control_panel_padding") {
+                let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                let val_str = rest.trim_end_matches('"').trim();
+                if let Ok(val) = val_str.parse::<f32>() {
+                    if let Ok(mut lock) = CONTROL_PANEL_PADDING.write() {
+                        *lock = val;
+                    }
+                }
+            }
+            if let Some(rest) = trimmed.strip_prefix("control_panel_gap") {
+                let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                let val_str = rest.trim_end_matches('"').trim();
+                if let Ok(val) = val_str.parse::<f32>() {
+                    if let Ok(mut lock) = CONTROL_PANEL_GAP.write() {
+                        *lock = val;
+                    }
+                }
+            }
             if let Some(rest) = trimmed.strip_prefix("section_padding") {
                 let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
                 let val_str = rest.trim_end_matches('"').trim();
@@ -1369,6 +1391,62 @@ pub fn set_column_gap(gap: f32) {
     }
 }
 
+pub fn control_panel_padding() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("control_panel_padding") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = CONTROL_PANEL_PADDING.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *CONTROL_PANEL_PADDING.read().unwrap()
+}
+
+pub fn set_control_panel_padding(padding: f32) {
+    if let Ok(mut lock) = CONTROL_PANEL_PADDING.write() {
+        *lock = padding;
+    }
+}
+
+pub fn control_panel_gap() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("control_panel_gap") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = CONTROL_PANEL_GAP.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *CONTROL_PANEL_GAP.read().unwrap()
+}
+
+pub fn set_control_panel_gap(gap: f32) {
+    if let Ok(mut lock) = CONTROL_PANEL_GAP.write() {
+        *lock = gap;
+    }
+}
+
 pub fn section_padding() -> f32 {
     use std::sync::Once;
     static INIT: Once = Once::new();
diff --git a/src/widget/container/control_panel.rs b/src/widget/container/control_panel.rs
index 0e2a1f4..363cef5 100644
--- a/src/widget/container/control_panel.rs
+++ b/src/widget/container/control_panel.rs
@@ -80,7 +80,7 @@ impl Element for ControlPanel {
     }
 
     fn color(&self) -> [f32; 4] {
-        colors::ramp_background_color()
+        colors::control_panel_color()
     }
 
     fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
@@ -95,8 +95,8 @@ impl Element for ControlPanel {
         let mut dummy = crate::context::UiContext::new();
         let self_ptr_option = Some(self_ptr);
 
-        let padding = 16.0;
-        let gap = crate::layout::column_gap();
+        let padding = crate::layout::control_panel_padding();
+        let gap = crate::layout::control_panel_gap();
         let mut col = ColumnLayout::new(x, y, w - 12.0, gap, padding); // 12px reserved for scrollbar track
 
         unsafe {
@@ -244,7 +244,7 @@ impl Element for ControlPanel {
         quads.push((x, y, w, h, 0.0, self.color(), (false, false, false, false)));
 
         // 2. Borders
-        let border_color = colors::ramp_border_color();
+        let border_color = colors::control_panel_border_color();
         quads.push((x, y, w, 1.0, 0.0, border_color, (false, false, false, false)));
         quads.push((x, y + h - 1.0, w, 1.0, 0.0, border_color, (false, false, false, false)));
         quads.push((x, y, 1.0, h, 0.0, border_color, (false, false, false, false)));