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

commitc5baa30d066bdfde9445917b4b449fabecd5e93c
parent46a38480e1
authorLucas Galante <[email protected]>
date2026-06-21 08:32
feat: add scrollinglist entry background and highlight color selectors

 src/input_handler.rs   | 14 ++++++--
 src/main.rs            | 30 +++++++++++++++-
 src/pages/interface.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 137 insertions(+), 4 deletions(-)

diff --git a/src/input_handler.rs b/src/input_handler.rs
index 71993c3..c623119 100644
--- a/src/input_handler.rs
+++ b/src/input_handler.rs
@@ -1017,6 +1017,7 @@ impl SystemInterface {
         if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Interface {
             for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
                 let old = cp.color;
+                let old_alpha = cp.alpha;
                 if !cp.hit_test(lx, ly, &self.ui_context) { cp.unfocus(); }
                 cp.mouse_input(button, state, lx, ly, &mut self.ui_context);
                 if cp.take_click() {
@@ -1042,10 +1043,12 @@ impl SystemInterface {
                         18 => pages::interface::InterfaceMessage::PickWindowColor,
                         19 => pages::interface::InterfaceMessage::PickPageColor,
                         20 => pages::interface::InterfaceMessage::PickLayerColor,
+                        21 => pages::interface::InterfaceMessage::PickScrollingListEntryBgColor,
+                        22 => pages::interface::InterfaceMessage::PickScrollingListEntryHighlightColor,
                         _ => pages::interface::InterfaceMessage::PickLowColor,
                     }));
                 }
-                if cp.color != old {
+                if cp.color != old || cp.alpha != old_alpha {
                     actions.push(AppAction::Interface(match i {
                         0 => pages::interface::InterfaceMessage::SetPageLowColor(cp.color),
                         1 => pages::interface::InterfaceMessage::SetHighColor(cp.color),
@@ -1068,6 +1071,8 @@ impl SystemInterface {
                         18 => pages::interface::InterfaceMessage::SetWindowColor(cp.color),
                         19 => pages::interface::InterfaceMessage::SetPageColor(cp.color),
                         20 => pages::interface::InterfaceMessage::SetLayerColor(cp.color),
+                        21 => pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
+                        22 => pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
                         _ => pages::interface::InterfaceMessage::SetDesktopBackground(cp.color),
                     }));
                 }
@@ -2011,6 +2016,8 @@ impl SystemInterface {
                             18 => pages::interface::InterfaceMessage::SetWindowColor(cp.color),
                             19 => pages::interface::InterfaceMessage::SetPageColor(cp.color),
                             20 => pages::interface::InterfaceMessage::SetLayerColor(cp.color),
+                            21 => pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
+                            22 => pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
                             _ => pages::interface::InterfaceMessage::SetDesktopBackground(cp.color),
                         }));
                     }
@@ -2690,8 +2697,9 @@ impl SystemInterface {
             let mut actions = Vec::new();
             for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
                 let old = cp.color;
+                let old_alpha = cp.alpha;
                 if cp.keyboard_input(event, &mut self.ui_context) {
-                    if cp.color != old {
+                    if cp.color != old || cp.alpha != old_alpha {
                         actions.push(AppAction::Interface(match i {
                             0 => pages::interface::InterfaceMessage::SetPageLowColor(cp.color),
                             1 => pages::interface::InterfaceMessage::SetHighColor(cp.color),
@@ -2714,6 +2722,8 @@ impl SystemInterface {
                             18 => pages::interface::InterfaceMessage::SetWindowColor(cp.color),
                             19 => pages::interface::InterfaceMessage::SetPageColor(cp.color),
                             20 => pages::interface::InterfaceMessage::SetLayerColor(cp.color),
+                            21 => pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
+                            22 => pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
                             _ => pages::interface::InterfaceMessage::SetDesktopBackground(cp.color),
                         }));
                     }
diff --git a/src/main.rs b/src/main.rs
index 8afb7bc..55e8a8b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -493,7 +493,35 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
                 20 => self.app.interface.layer_color,
                 _ => self.app.interface.desktop_background_color,
             };
-            if cp.color != state_color {
+            if i == 21 || i == 22 {
+                let (state_rgb, state_alpha) = if i == 21 {
+                    (
+                        [
+                            self.app.interface.scrollinglist_entry_bg_color[0],
+                            self.app.interface.scrollinglist_entry_bg_color[1],
+                            self.app.interface.scrollinglist_entry_bg_color[2],
+                        ],
+                        self.app.interface.scrollinglist_entry_bg_color[3],
+                    )
+                } else {
+                    (
+                        [
+                            self.app.interface.scrollinglist_entry_highlight_color[0],
+                            self.app.interface.scrollinglist_entry_highlight_color[1],
+                            self.app.interface.scrollinglist_entry_highlight_color[2],
+                        ],
+                        self.app.interface.scrollinglist_entry_highlight_color[3],
+                    )
+                };
+                if cp.color != state_rgb || cp.alpha != state_alpha {
+                    color_actions.push(AppAction::Interface(if i == 21 {
+                        pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha])
+                    } else {
+                        pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha])
+                    }));
+                    color_changed = true;
+                }
+            } else if cp.color != state_color {
                 color_actions.push(AppAction::Interface(match i {
                     0 => pages::interface::InterfaceMessage::SetPageLowColor(cp.color),
                     1 => pages::interface::InterfaceMessage::SetHighColor(cp.color),
diff --git a/src/pages/interface.rs b/src/pages/interface.rs
index 6cb5bb3..901dab6 100644
--- a/src/pages/interface.rs
+++ b/src/pages/interface.rs
@@ -180,6 +180,8 @@ pub struct InterfaceState {
     pub toggle_enabled_color: [u8; 3],
     pub toggle_disabled_color: [u8; 3],
     pub scrollinglist_bg_color: [u8; 3],
+    pub scrollinglist_entry_bg_color: [u8; 4],
+    pub scrollinglist_entry_highlight_color: [u8; 4],
     pub breadcrumb_bg_color: [u8; 3],
     pub popover_bg_color: [u8; 3],
     pub color_selectors: Vec<ColorSelector>,
@@ -331,6 +333,8 @@ impl Default for InterfaceState {
             toggle_enabled_color: [104, 217, 165],
             toggle_disabled_color: [135, 135, 148],
             scrollinglist_bg_color: [81, 81, 97],
+            scrollinglist_entry_bg_color: [255, 255, 255, 10],
+            scrollinglist_entry_highlight_color: [255, 255, 255, 204],
             breadcrumb_bg_color: [81, 81, 97],
             popover_bg_color: [81, 81, 97],
             color_selectors: vec![
@@ -355,6 +359,8 @@ impl Default for InterfaceState {
                 ColorSelector::new([0x0a, 0x1a, 0x0e]).with_label("Color"), // 18: Surfaces - Window Color
                 ColorSelector::new([0, 0, 0]).with_label("Page Color"), // 19: Containers - Page Color
                 ColorSelector::new([0, 0, 0]).with_label("Layer Color"), // 20: Containers - Layer Color
+                ColorSelector::new_rgba([255, 255, 255, 10]).with_label("Entry Background"), // 21: ScrollingList - Entry Background
+                ColorSelector::new_rgba([255, 255, 255, 204]).with_label("Entry Highlight"), // 22: ScrollingList - Entry Highlight
             ],
             paginator_tab_margin_x: 5,
             paginator_tab_margin_y: 10,
@@ -506,6 +512,8 @@ pub enum InterfaceMessage {
     SetToggleEnabledColor([u8; 3]),
     SetToggleDisabledColor([u8; 3]),
     SetScrollingListBgColor([u8; 3]),
+    SetScrollingListEntryBgColor([u8; 4]),
+    SetScrollingListEntryHighlightColor([u8; 4]),
     SetBreadcrumbBgColor([u8; 3]),
     SetPopoverBgColor([u8; 3]),
     SetNotificationBgColor([u8; 3]),
@@ -574,6 +582,8 @@ pub enum InterfaceMessage {
     PickToggleEnabledColor,
     PickToggleDisabledColor,
     PickScrollingListBgColor,
+    PickScrollingListEntryBgColor,
+    PickScrollingListEntryHighlightColor,
     PickBreadcrumbBgColor,
     PickPopoverBgColor,
     PickNotificationBgColor,
@@ -637,6 +647,8 @@ pub fn read_interface_config() -> InterfaceState {
     let toggle_disabled = parse_color_from_key(&content, "toggle_disabled_color", [135, 135, 148]);
 
     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]);
 
     let breadcrumb_bg = parse_color_from_key(&content, "breadcrumb_bg_color", scrollinglist_bg);
 
@@ -715,6 +727,8 @@ pub fn read_interface_config() -> InterfaceState {
         toggle_enabled_color: toggle_enabled,
         toggle_disabled_color: toggle_disabled,
         scrollinglist_bg_color: scrollinglist_bg,
+        scrollinglist_entry_bg_color: scrollinglist_entry_bg,
+        scrollinglist_entry_highlight_color: scrollinglist_entry_highlight,
         breadcrumb_bg_color: breadcrumb_bg,
         popover_bg_color: popover_bg,
         color_selectors: vec![
@@ -739,6 +753,8 @@ pub fn read_interface_config() -> InterfaceState {
             ColorSelector::new(window_color).with_label("Color").with_font_family(&color_selector_font), // 18: Surfaces - Window Color
             ColorSelector::new(page_color).with_label("Page Color").with_font_family(&color_selector_font), // 19: Containers - Page Color
             ColorSelector::new(layer_color).with_label("Layer Color").with_font_family(&color_selector_font), // 20: Containers - Layer Color
+            ColorSelector::new_rgba(scrollinglist_entry_bg).with_label("Entry Background").with_font_family(&color_selector_font), // 21: ScrollingList - Entry Background
+            ColorSelector::new_rgba(scrollinglist_entry_highlight).with_label("Entry Highlight").with_font_family(&color_selector_font), // 22: ScrollingList - Entry Highlight
         ],
         paginator_tab_margin_x,
         paginator_tab_margin_y,
@@ -1251,6 +1267,33 @@ fn parse_hex(s: &str) -> [u8; 3] {
     } else { [0x0a, 0x1a, 0x0e] }
 }
 
+fn parse_rgba_color_from_key(content: &str, key: &str, default: [u8; 4]) -> [u8; 4] {
+    let val = parse_json(content);
+    if let Some(v) = json_find_key(&val, key) {
+        if let Some(s) = v.as_str() {
+            return parse_hex_rgba(s);
+        }
+    }
+    default
+}
+
+fn parse_hex_rgba(s: &str) -> [u8; 4] {
+    let s = s.trim_matches(|c| c == '"' || c == '\'' || c == ' ');
+    let s = s.trim_start_matches('#');
+    if s.len() >= 8 {
+        let r = u8::from_str_radix(&s[0..2], 16).unwrap_or(255);
+        let g = u8::from_str_radix(&s[2..4], 16).unwrap_or(255);
+        let b = u8::from_str_radix(&s[4..6], 16).unwrap_or(255);
+        let a = u8::from_str_radix(&s[6..8], 16).unwrap_or(255);
+        [r, g, b, a]
+    } else if s.len() >= 6 {
+        let r = u8::from_str_radix(&s[0..2], 16).unwrap_or(255);
+        let g = u8::from_str_radix(&s[2..4], 16).unwrap_or(255);
+        let b = u8::from_str_radix(&s[4..6], 16).unwrap_or(255);
+        [r, g, b, 255]
+    } else { [255, 255, 255, 255] }
+}
+
 pub fn write_config_value(key: &str, value: &str) -> bool {
     write_config_value_path(CONFIG_PATH, key, value)
 }
@@ -1764,6 +1807,26 @@ fn apply_scrollinglist_bg_color(rgb: [u8; 3]) {
     cce_ui::color::set_scrollinglist_bg_color([r, g, b, 0.3]);
 }
 
+fn apply_scrollinglist_entry_bg_color(rgba: [u8; 4]) {
+    let hex = format!("\"#{:02x}{:02x}{:02x}{:02x}\"", rgba[0], rgba[1], rgba[2], rgba[3]);
+    write_config_value("scrollinglist_entry_bg_color", &hex);
+    let r = cce_ui::color::srgb_to_linear(rgba[0] as f32 / 255.0);
+    let g = cce_ui::color::srgb_to_linear(rgba[1] as f32 / 255.0);
+    let b = cce_ui::color::srgb_to_linear(rgba[2] as f32 / 255.0);
+    let a = rgba[3] as f32 / 255.0;
+    cce_ui::color::set_scrollinglist_entry_bg_color([r, g, b, a]);
+}
+
+fn apply_scrollinglist_entry_highlight_color(rgba: [u8; 4]) {
+    let hex = format!("\"#{:02x}{:02x}{:02x}{:02x}\"", rgba[0], rgba[1], rgba[2], rgba[3]);
+    write_config_value("scrollinglist_entry_highlight_color", &hex);
+    let r = cce_ui::color::srgb_to_linear(rgba[0] as f32 / 255.0);
+    let g = cce_ui::color::srgb_to_linear(rgba[1] as f32 / 255.0);
+    let b = cce_ui::color::srgb_to_linear(rgba[2] as f32 / 255.0);
+    let a = rgba[3] as f32 / 255.0;
+    cce_ui::color::set_scrollinglist_entry_highlight_color([r, g, b, a]);
+}
+
 fn apply_breadcrumb_bg_color(rgb: [u8; 3]) {
     let hex = format!("\"#{:02x}{:02x}{:02x}\"", rgb[0], rgb[1], rgb[2]);
     write_config_value("breadcrumb_bg_color", &hex);
@@ -2851,6 +2914,18 @@ pub fn view(state: &mut InterfaceState, cx: f32, cy: f32, cw: f32, ch: f32, sec_
             state.color_selectors[14].color = state.scrollinglist_bg_color;
             subsec.widget_full(&mut state.color_selectors[14], 40.0, ctx);
             subsec.spacing(8.0);
+
+            let bg_rgba = state.scrollinglist_entry_bg_color;
+            state.color_selectors[21].color = [bg_rgba[0], bg_rgba[1], bg_rgba[2]];
+            state.color_selectors[21].alpha = bg_rgba[3];
+            subsec.widget_full(&mut state.color_selectors[21], 40.0, ctx);
+            subsec.spacing(8.0);
+
+            let highlight_rgba = state.scrollinglist_entry_highlight_color;
+            state.color_selectors[22].color = [highlight_rgba[0], highlight_rgba[1], highlight_rgba[2]];
+            state.color_selectors[22].alpha = highlight_rgba[3];
+            subsec.widget_full(&mut state.color_selectors[22], 40.0, ctx);
+            subsec.spacing(8.0);
         });
         sec.spacing(12.0);
 
@@ -3081,6 +3156,14 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
             state.scrollinglist_bg_color = rgb;
             apply_scrollinglist_bg_color(rgb);
         }
+        InterfaceMessage::SetScrollingListEntryBgColor(rgba) => {
+            state.scrollinglist_entry_bg_color = rgba;
+            apply_scrollinglist_entry_bg_color(rgba);
+        }
+        InterfaceMessage::SetScrollingListEntryHighlightColor(rgba) => {
+            state.scrollinglist_entry_highlight_color = rgba;
+            apply_scrollinglist_entry_highlight_color(rgba);
+        }
         InterfaceMessage::SetBreadcrumbBgColor(rgb) => {
             state.breadcrumb_bg_color = rgb;
             apply_breadcrumb_bg_color(rgb);
@@ -3355,7 +3438,7 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
             status_interface_reload();
             propagate_links(state, "menubar_opacity", &opacity.to_string());
         }
-        InterfaceMessage::PickLowColor | InterfaceMessage::PickHighColor | InterfaceMessage::PickDisabledColor | InterfaceMessage::PickSeparatorColor | InterfaceMessage::PickVisualGuides | InterfaceMessage::PickSliderTrackColor | InterfaceMessage::PickPageLowColor | InterfaceMessage::PickColorBordersColor | InterfaceMessage::PickNormalColor | InterfaceMessage::PickPaginatorSidebarColor | InterfaceMessage::PickPrimaryHighlightColor | InterfaceMessage::PickMenubarTabLabelColor | InterfaceMessage::PickToggleEnabledColor | InterfaceMessage::PickToggleDisabledColor | InterfaceMessage::PickScrollingListBgColor | InterfaceMessage::PickBreadcrumbBgColor | InterfaceMessage::PickPopoverBgColor | InterfaceMessage::PickNotificationBgColor | InterfaceMessage::PickWindowColor | InterfaceMessage::PickPageColor | InterfaceMessage::PickLayerColor => {}
+        InterfaceMessage::PickLowColor | InterfaceMessage::PickHighColor | InterfaceMessage::PickDisabledColor | InterfaceMessage::PickSeparatorColor | InterfaceMessage::PickVisualGuides | InterfaceMessage::PickSliderTrackColor | InterfaceMessage::PickPageLowColor | InterfaceMessage::PickColorBordersColor | InterfaceMessage::PickNormalColor | InterfaceMessage::PickPaginatorSidebarColor | InterfaceMessage::PickPrimaryHighlightColor | InterfaceMessage::PickMenubarTabLabelColor | InterfaceMessage::PickToggleEnabledColor | InterfaceMessage::PickToggleDisabledColor | InterfaceMessage::PickScrollingListBgColor | InterfaceMessage::PickScrollingListEntryBgColor | InterfaceMessage::PickScrollingListEntryHighlightColor | InterfaceMessage::PickBreadcrumbBgColor | InterfaceMessage::PickPopoverBgColor | InterfaceMessage::PickNotificationBgColor | InterfaceMessage::PickWindowColor | InterfaceMessage::PickPageColor | InterfaceMessage::PickLayerColor => {}
         InterfaceMessage::Refreshed(new) => {
             let was_mx_hovered = state.tab_margin_spinbox_x.hovered();
             let was_my_hovered = state.tab_margin_spinbox_y.hovered();
@@ -4146,6 +4229,18 @@ mod tests {
         assert_eq!(parse_color_from_key(content, "non_existent", [1, 2, 3]), [1, 2, 3]);
     }
 
+    #[test]
+    fn test_parse_rgba_color_from_key() {
+        let content = r##"{
+            "layout": {
+                "scrollinglist_entry_bg_color": "#ffffff0a",
+                "scrollinglist_entry_highlight_color": "#ffffffcc"
+            }
+        }"##;
+        assert_eq!(parse_rgba_color_from_key(content, "scrollinglist_entry_bg_color", [0, 0, 0, 0]), [255, 255, 255, 10]);
+        assert_eq!(parse_rgba_color_from_key(content, "scrollinglist_entry_highlight_color", [0, 0, 0, 0]), [255, 255, 255, 204]);
+    }
+
     #[test]
     fn test_write_config_value_path() {
         let dir = std::env::temp_dir();