git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commit381e1fd413aad1652177c636635d1fc78198b05d
parente6f793a1d7
authorLucas Galante <[email protected]>
date2026-06-28 05:33
Add Toggle background color, border width, and font settings

 src/pages/interface.rs | 180 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 179 insertions(+), 1 deletion(-)

diff --git a/src/pages/interface.rs b/src/pages/interface.rs
index 27a0a40..cfb31a3 100644
--- a/src/pages/interface.rs
+++ b/src/pages/interface.rs
@@ -237,6 +237,7 @@ pub struct InterfaceState {
     pub menubar_tab_label_color: [u8; 3],
     pub toggle_enabled_color: [u8; 3],
     pub toggle_disabled_color: [u8; 3],
+    pub toggle_bg_color: [u8; 3],
     pub scrollinglist_bg_color: [u8; 3],
     pub scrollinglist_entry_bg_color: [u8; 4],
     pub scrollinglist_entry_highlight_color: [u8; 4],
@@ -286,6 +287,8 @@ pub struct InterfaceState {
     pub toggle_height_spinbox: Spinbox,
     pub toggle_corner_radius: u16,
     pub toggle_corner_radius_spinbox: Spinbox,
+    pub toggle_border_width: u16,
+    pub toggle_border_width_spinbox: Spinbox,
 
     pub color_selector_height: u16,
     pub color_selector_height_spinbox: Spinbox,
@@ -363,6 +366,8 @@ pub struct InterfaceState {
     pub nested_section_label_font_selector: FontSelector,
     pub breadcrumb_font: String,
     pub breadcrumb_font_selector: FontSelector,
+    pub toggle_font: String,
+    pub toggle_font_selector: FontSelector,
     // Graph configuration fields
     pub graph_show_grid: bool,
     pub graph_show_grid_toggle: Toggle,
@@ -401,6 +406,7 @@ impl Default for InterfaceState {
             menubar_tab_label_color: [230, 230, 242],
             toggle_enabled_color: [104, 217, 165],
             toggle_disabled_color: [135, 135, 148],
+            toggle_bg_color: [116, 116, 128],
             scrollinglist_bg_color: [81, 81, 97],
             scrollinglist_entry_bg_color: [255, 255, 255, 10],
             scrollinglist_entry_highlight_color: [255, 255, 255, 204],
@@ -431,6 +437,7 @@ impl Default for InterfaceState {
                 ColorSelector::new_rgba([255, 255, 255, 204]).with_label("Entry Highlight").with_config(CONFIG_PATH, "scrollinglist_entry_highlight_color"), // 22: ScrollingList - Entry Highlight
                 ColorSelector::new([0x15, 0x15, 0x20]).with_label("Background Color").with_config(CONFIG_PATH, "status_box_background_color"), // 23: Status - Background Color
                 ColorSelector::new_rgba([255, 255, 255, 13]).with_label("Grid Color").with_config(CONFIG_PATH, "desktop_grid_color"), // 24: Surfaces - Desktop Grid Color
+                ColorSelector::new([116, 116, 128]).with_label("Background").with_config(CONFIG_PATH, "toggle_bg_color"), // 24: Toggles - Background
             ],
             paginator_tab_padding_x: 10,
             paginator_tab_padding_y: 14,
@@ -467,6 +474,8 @@ impl Default for InterfaceState {
             toggle_height_spinbox: Spinbox::new(44, 10, 100, 1).with_label("Height").with_unit("px").with_config(CONFIG_PATH, "toggle_height"),
             toggle_corner_radius: 4,
             toggle_corner_radius_spinbox: Spinbox::new(4, 0, 50, 1).with_label("Corner Radius").with_unit("px").with_config(CONFIG_PATH, "toggle_corner_radius"),
+            toggle_border_width: 1,
+            toggle_border_width_spinbox: Spinbox::new(1, 0, 10, 1).with_label("Border Width").with_unit("px").with_config(CONFIG_PATH, "toggle_border_width"),
 
             color_selector_height: 22,
             color_selector_height_spinbox: Spinbox::new(22, 10, 100, 1).with_label("Height").with_unit("px").with_config(CONFIG_PATH, "color_selector_height"),
@@ -539,6 +548,8 @@ impl Default for InterfaceState {
             nested_section_label_font_selector: FontSelector::new("Outfit".to_string()).with_label("Label").with_config(CONFIG_PATH, "nested_section_label_font"),
             breadcrumb_font: "Outfit".to_string(),
             breadcrumb_font_selector: FontSelector::new("Outfit".to_string()).with_label("Font").with_config(CONFIG_PATH, "breadcrumb_font"),
+            toggle_font: "Outfit".to_string(),
+            toggle_font_selector: FontSelector::new("Outfit".to_string()).with_label("Font").with_config(CONFIG_PATH, "toggle_font"),
             graph_show_grid: true,
             graph_show_grid_toggle: Toggle::new().with_label("Show Grid").with_config(CONFIG_PATH, "graph_show_grid"),
             graph_snap_enabled: true,
@@ -622,6 +633,9 @@ pub enum InterfaceMessage {
     SetSpinboxCornerRadius(u16),
     SetToggleHeight(u16),
     SetToggleCornerRadius(u16),
+    SetToggleBgColor([u8; 3]),
+    SetToggleBorderWidth(u16),
+    SetToggleFont(String),
 
     SetColorSelectorHeight(u16),
     SetColorSelectorCornerRadius(u16),
@@ -730,6 +744,8 @@ pub fn read_interface_config() -> InterfaceState {
 
     let toggle_disabled = parse_color_from_key(&content, "toggle_disabled_color", [135, 135, 148]);
 
+    let toggle_bg = parse_color_from_key(&content, "toggle_bg_color", [116, 116, 128]);
+
     let scrollinglist_bg = parse_color_from_key(&content, "scrollinglist_bg_color", [81, 81, 97]);
     let scrollinglist_entry_bg = parse_rgba_color_from_key(&content, "scrollinglist_entry_bg_color", [255, 255, 255, 10]);
     let scrollinglist_entry_highlight = parse_rgba_color_from_key(&content, "scrollinglist_entry_highlight_color", [255, 255, 255, 204]);
@@ -758,6 +774,7 @@ pub fn read_interface_config() -> InterfaceState {
     let spinbox_corner_radius = parse_u16_from(&content, "spinbox_corner_radius", 4);
     let toggle_height = parse_u16_from(&content, "toggle_height", 44);
     let toggle_corner_radius = parse_u16_from(&content, "toggle_corner_radius", 4);
+    let toggle_border_width = parse_u16_from(&content, "toggle_border_width", 1);
 
     let color_selector_height = parse_u16_from(&content, "color_selector_height", 22);
     let color_selector_corner_radius = parse_u16_from(&content, "color_selector_corner_radius", 4);
@@ -777,6 +794,16 @@ pub fn read_interface_config() -> InterfaceState {
     let section_label_font = parse_string_from(&content, "section_label_font", "Outfit");
     let nested_section_label_font = parse_string_from(&content, "nested_section_label_font", "Outfit");
     let breadcrumb_font = parse_string_from(&content, "breadcrumb_font", "Outfit");
+    let toggle_font = parse_string_from(&content, "toggle_font", "Outfit");
+
+    cce_ui::layout::set_toggle_border_width(toggle_border_width as f32);
+    cce_ui::layout::set_toggle_font(&toggle_font);
+    cce_ui::color::set_toggle_bg_color(cce_ui::color::to_linear([
+        toggle_bg[0] as f32 / 255.0,
+        toggle_bg[1] as f32 / 255.0,
+        toggle_bg[2] as f32 / 255.0,
+        1.0,
+    ]));
     let nested_section_label_alignment = parse_u16_from(&content, "nested_section_label_alignment", 0) as u8;
     let nested_section_label_offset = parse_i16_from(&content, "nested_section_label_offset", 0);
     let label_margin = parse_u16_from(&content, "label_margin", 6);
@@ -821,6 +848,7 @@ pub fn read_interface_config() -> InterfaceState {
         menubar_tab_label_color: menubar_tab_label,
         toggle_enabled_color: toggle_enabled,
         toggle_disabled_color: toggle_disabled,
+        toggle_bg_color: toggle_bg,
         scrollinglist_bg_color: scrollinglist_bg,
         scrollinglist_entry_bg_color: scrollinglist_entry_bg,
         scrollinglist_entry_highlight_color: scrollinglist_entry_highlight,
@@ -849,8 +877,8 @@ pub fn read_interface_config() -> InterfaceState {
             ColorSelector::new(layer_color).with_label("Layer Color").with_font_family(&color_selector_font).with_config(CONFIG_PATH, "layer_color"), // 20: Containers - Layer Color
             ColorSelector::new_rgba(scrollinglist_entry_bg).with_label("Entry Background").with_font_family(&color_selector_font).with_config(CONFIG_PATH, "scrollinglist_entry_bg_color"), // 21: ScrollingList - Entry Background
             ColorSelector::new_rgba(scrollinglist_entry_highlight).with_label("Entry Highlight").with_font_family(&color_selector_font).with_config(CONFIG_PATH, "scrollinglist_entry_highlight_color"), // 22: ScrollingList - Entry Highlight
-            ColorSelector::new(status_box_background_color).with_label("Background Color").with_font_family(&color_selector_font).with_config(CONFIG_PATH, "status_box_background_color"), // 23: Status - Background Color
             ColorSelector::new_rgba(desktop_grid_color).with_label("Grid Color").with_font_family(&color_selector_font).with_config(CONFIG_PATH, "desktop_grid_color"), // 24: Surfaces - Desktop Grid Color
+            ColorSelector::new(toggle_bg).with_label("Background").with_font_family(&color_selector_font).with_config(CONFIG_PATH, "toggle_bg_color"), // 24: Toggles - Background
         ],
         paginator_tab_padding_x,
         paginator_tab_padding_y,
@@ -887,6 +915,8 @@ pub fn read_interface_config() -> InterfaceState {
         toggle_height_spinbox: Spinbox::new(toggle_height as i32, 10, 100, 1).with_label("Height").with_unit("px").with_config(CONFIG_PATH, "toggle_height"),
         toggle_corner_radius,
         toggle_corner_radius_spinbox: Spinbox::new(toggle_corner_radius as i32, 0, 50, 1).with_label("Corner Radius").with_unit("px").with_config(CONFIG_PATH, "toggle_corner_radius"),
+        toggle_border_width,
+        toggle_border_width_spinbox: Spinbox::new(toggle_border_width as i32, 0, 10, 1).with_label("Border Width").with_unit("px").with_config(CONFIG_PATH, "toggle_border_width"),
 
         color_selector_height,
         color_selector_height_spinbox: Spinbox::new(color_selector_height as i32, 10, 100, 1).with_label("Height").with_unit("px").with_config(CONFIG_PATH, "color_selector_height"),
@@ -966,6 +996,8 @@ pub fn read_interface_config() -> InterfaceState {
         nested_section_label_font_selector: FontSelector::new(nested_section_label_font.clone()).with_label("Label").with_config(CONFIG_PATH, "nested_section_label_font"),
         breadcrumb_font: breadcrumb_font.clone(),
         breadcrumb_font_selector: FontSelector::new(breadcrumb_font.clone()).with_label("Font").with_config(CONFIG_PATH, "breadcrumb_font"),
+        toggle_font: toggle_font.clone(),
+        toggle_font_selector: FontSelector::new(toggle_font.clone()).with_label("Font").with_config(CONFIG_PATH, "toggle_font"),
         graph_show_grid,
         graph_show_grid_toggle: Toggle::new().with_label("Show Grid").with_config(CONFIG_PATH, "graph_show_grid"),
         graph_snap_enabled,
@@ -1373,6 +1405,26 @@ pub fn propagate_links(state: &mut InterfaceState, key: &str, val_str: &str) {
                     apply_toggle_corner_radius(val);
                 }
             }
+            "toggle_bg_color" => {
+                let trimmed = val_str.trim().trim_matches('"');
+                let rgb = parse_hex(trimmed);
+                state.toggle_bg_color = rgb;
+                state.color_selectors[24].color = rgb;
+                apply_toggle_bg_color(rgb);
+            }
+            "toggle_border_width" => {
+                if let Ok(val) = val_str.parse::<u16>() {
+                    state.toggle_border_width = val;
+                    state.toggle_border_width_spinbox.value = val as i32;
+                    apply_toggle_border_width(val);
+                }
+            }
+            "toggle_font" => {
+                let font = val_str.trim_matches('"').to_string();
+                state.toggle_font = font.clone();
+                state.toggle_font_selector.font_family = font.clone();
+                apply_toggle_font(&font);
+            }
 
             "color_selector_height" => {
                 if let Ok(val) = val_str.parse::<u16>() {
@@ -1951,6 +2003,27 @@ fn apply_toggle_corner_radius(radius: u16) {
     cce_ui::layout::set_toggle_corner_radius(radius as f32);
 }
 
+fn apply_toggle_border_width(width: u16) {
+    write_config_value("toggle_border_width", &width.to_string());
+    cce_ui::layout::set_toggle_border_width(width as f32);
+}
+
+fn apply_toggle_bg_color(rgb: [u8; 3]) {
+    let hex = format!("#{:02x}{:02x}{:02x}", rgb[0], rgb[1], rgb[2]);
+    write_config_value("toggle_bg_color", &hex);
+    cce_ui::color::set_toggle_bg_color(cce_ui::color::to_linear([
+        rgb[0] as f32 / 255.0,
+        rgb[1] as f32 / 255.0,
+        rgb[2] as f32 / 255.0,
+        1.0,
+    ]));
+}
+
+fn apply_toggle_font(font: &str) {
+    write_config_value("toggle_font", &format!("\"{}\"", font));
+    cce_ui::layout::set_toggle_font(font);
+}
+
 
 fn apply_color_selector_height(height: u16) {
     write_config_value("color_selector_height", &height.to_string());
@@ -2525,10 +2598,16 @@ pub fn view(state: &mut InterfaceState, cx: f32, cy: f32, cw: f32, ch: f32, sec_
             subsec.widget_full(&mut state.color_selectors[11], 40.0, ctx);
             state.color_selectors[12].color = state.toggle_disabled_color;
             subsec.widget_full(&mut state.color_selectors[12], 40.0, ctx);
+            state.color_selectors[24].color = state.toggle_bg_color;
+            subsec.widget_full(&mut state.color_selectors[24], 40.0, ctx);
             state.toggle_height_spinbox.value = state.toggle_height as i32;
             subsec.widget_full(&mut state.toggle_height_spinbox, 44.0, ctx);
             state.toggle_corner_radius_spinbox.value = state.toggle_corner_radius as i32;
             subsec.widget_full(&mut state.toggle_corner_radius_spinbox, 44.0, ctx);
+            state.toggle_border_width_spinbox.value = state.toggle_border_width as i32;
+            subsec.widget_full(&mut state.toggle_border_width_spinbox, 44.0, ctx);
+            state.toggle_font_selector.font_family = state.toggle_font.clone();
+            subsec.widget_full(&mut state.toggle_font_selector, 44.0, ctx);
         });
 
         // Breadcrumb Section
@@ -3122,6 +3201,22 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
             apply_toggle_corner_radius(radius);
             propagate_links(state, "toggle_corner_radius", &radius.to_string());
         }
+        InterfaceMessage::SetToggleBgColor(rgb) => {
+            state.toggle_bg_color = rgb;
+            apply_toggle_bg_color(rgb);
+            let hex = format!("#{:02x}{:02x}{:02x}", rgb[0], rgb[1], rgb[2]);
+            propagate_links(state, "toggle_bg_color", &hex);
+        }
+        InterfaceMessage::SetToggleBorderWidth(width) => {
+            state.toggle_border_width = width;
+            apply_toggle_border_width(width);
+            propagate_links(state, "toggle_border_width", &width.to_string());
+        }
+        InterfaceMessage::SetToggleFont(font) => {
+            state.toggle_font = font.clone();
+            apply_toggle_font(&font);
+            propagate_links(state, "toggle_font", &format!("\"{}\"", font));
+        }
 
         InterfaceMessage::SetColorSelectorHeight(height) => {
             state.color_selector_height = height;
@@ -4355,6 +4450,75 @@ mod tests {
         let _ = fs::remove_file(path_str);
     }
 
+    #[test]
+    fn test_read_write_toggle_bg_color() {
+        let dir = std::env::temp_dir();
+        let path = dir.join("test_toggle_bg_color_config.json");
+        let path_str = path.to_str().unwrap();
+
+        let initial_content = "{\"layout\": {\"gap\": 18}}";
+        fs::write(path_str, initial_content).unwrap();
+
+        let content = fs::read_to_string(path_str).unwrap();
+        let val = parse_color_from_key(&content, "toggle_bg_color", [116, 116, 128]);
+        assert_eq!(val, [116, 116, 128]);
+
+        assert!(write_config_value_path(path_str, "toggle_bg_color", "\"#123456\""));
+        let updated = fs::read_to_string(path_str).unwrap();
+        assert!(updated.contains("\"toggle_bg_color\": \"#123456\""));
+
+        let val2 = parse_color_from_key(&updated, "toggle_bg_color", [116, 116, 128]);
+        assert_eq!(val2, [18, 52, 86]);
+
+        let _ = fs::remove_file(path_str);
+    }
+
+    #[test]
+    fn test_read_write_toggle_border_width() {
+        let dir = std::env::temp_dir();
+        let path = dir.join("test_toggle_border_width_config.json");
+        let path_str = path.to_str().unwrap();
+
+        let initial_content = "{\"layout\": {\"gap\": 18}}";
+        fs::write(path_str, initial_content).unwrap();
+
+        let content = fs::read_to_string(path_str).unwrap();
+        let val = parse_u16_from(&content, "toggle_border_width", 1);
+        assert_eq!(val, 1);
+
+        assert!(write_config_value_path(path_str, "toggle_border_width", "3"));
+        let updated = fs::read_to_string(path_str).unwrap();
+        assert!(updated.contains("\"toggle_border_width\": 3"));
+
+        let val2 = parse_u16_from(&updated, "toggle_border_width", 1);
+        assert_eq!(val2, 3);
+
+        let _ = fs::remove_file(path_str);
+    }
+
+    #[test]
+    fn test_read_write_toggle_font() {
+        let dir = std::env::temp_dir();
+        let path = dir.join("test_toggle_font_config.json");
+        let path_str = path.to_str().unwrap();
+
+        let initial_content = "{\"layout\": {\"gap\": 18}}";
+        fs::write(path_str, initial_content).unwrap();
+
+        let content = fs::read_to_string(path_str).unwrap();
+        let val = parse_string_from(&content, "toggle_font", "Outfit");
+        assert_eq!(val, "Outfit");
+
+        assert!(write_config_value_path(path_str, "toggle_font", "\"Inter\""));
+        let updated = fs::read_to_string(path_str).unwrap();
+        assert!(updated.contains("\"toggle_font\": \"Inter\""));
+
+        let val2 = parse_string_from(&updated, "toggle_font", "Outfit");
+        assert_eq!(val2, "Inter");
+
+        let _ = fs::remove_file(path_str);
+    }
+
     #[test]
     fn test_read_write_slider_corner_radius() {
         let dir = std::env::temp_dir();
@@ -5500,6 +5664,8 @@ impl crate::pages::AppPage for InterfaceState {
         self.toggle_height_spinbox.set_parent(None, ctx);
         self.toggle_corner_radius_spinbox.clear_children(ctx);
         self.toggle_corner_radius_spinbox.set_parent(None, ctx);
+        self.toggle_border_width_spinbox.clear_children(ctx);
+        self.toggle_border_width_spinbox.set_parent(None, ctx);
 
         self.color_selector_height_spinbox.clear_children(ctx);
         self.color_selector_height_spinbox.set_parent(None, ctx);
@@ -5532,6 +5698,8 @@ impl crate::pages::AppPage for InterfaceState {
         self.menubar_font_selector.set_parent(None, ctx);
         self.breadcrumb_font_selector.clear_children(ctx);
         self.breadcrumb_font_selector.set_parent(None, ctx);
+        self.toggle_font_selector.clear_children(ctx);
+        self.toggle_font_selector.set_parent(None, ctx);
         self.section_label_font_selector.clear_children(ctx);
         self.section_label_font_selector.set_parent(None, ctx);
         self.nested_section_label_font_selector.clear_children(ctx);
@@ -5679,6 +5847,9 @@ impl crate::pages::AppPage for InterfaceState {
         cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.color_selectors[12], ctx);
         cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.toggle_height_spinbox, ctx);
         cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.toggle_corner_radius_spinbox, ctx);
+        cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.color_selectors[24], ctx);
+        cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.toggle_border_width_spinbox, ctx);
+        cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.toggle_font_selector, ctx);
         // Breadcrumb
         cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.color_selectors[14], ctx);
         cce_ui::widget::link_parent_child(&mut sec_containers[3], &mut self.breadcrumb_font_selector, ctx);
@@ -5832,6 +6003,7 @@ impl crate::pages::AppPage for InterfaceState {
                     21 => InterfaceMessage::SetLayerColor(cp.color),
                     22 => InterfaceMessage::SetStatusBoxBackgroundColor(cp.color),
                     23 => InterfaceMessage::SetDesktopGridColor([cp.color[0], cp.color[1], cp.color[2], 255]),
+                    24 => InterfaceMessage::SetToggleBgColor(cp.color),
                     _ => return,
                 }));
             }
@@ -5907,6 +6079,9 @@ impl crate::pages::AppPage for InterfaceState {
         if self.toggle_corner_radius_spinbox.take_change() {
             actions.push(AppAction::Interface(InterfaceMessage::SetToggleCornerRadius(self.toggle_corner_radius_spinbox.value as u16)));
         }
+        if self.toggle_border_width_spinbox.take_change() {
+            actions.push(AppAction::Interface(InterfaceMessage::SetToggleBorderWidth(self.toggle_border_width_spinbox.value as u16)));
+        }
         if self.color_selector_height_spinbox.take_change() {
             actions.push(AppAction::Interface(InterfaceMessage::SetColorSelectorHeight(self.color_selector_height_spinbox.value as u16)));
         }
@@ -6053,6 +6228,9 @@ impl crate::pages::AppPage for InterfaceState {
         if self.breadcrumb_font_selector.take_change() {
             actions.push(AppAction::Interface(InterfaceMessage::SetBreadcrumbFont(self.breadcrumb_font_selector.font_family.clone())));
         }
+        if self.toggle_font_selector.take_change() {
+            actions.push(AppAction::Interface(InterfaceMessage::SetToggleFont(self.toggle_font_selector.font_family.clone())));
+        }
         if self.section_label_font_selector.take_change() {
             actions.push(AppAction::Interface(InterfaceMessage::SetSectionLabelFont(self.section_label_font_selector.font_family.clone())));
         }