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

commit6844d902a800f3282b1a3e774ac9439a132e2ffb
parent6f5863f8be
authorLucas Galante <[email protected]>
date2026-07-02 21:47
Refactor ScrollingList to List and fix window decoration blur and styling

 src/widget/container/backplate.rs      |  5 +--
 src/widget/container/breadcrumb.rs     | 74 +++++++++++++++++++++++++---------
 src/widget/container/mod.rs            |  2 +-
 src/widget/container/scrolling_list.rs | 14 +++----
 src/widget/mod.rs                      |  2 +-
 5 files changed, 65 insertions(+), 32 deletions(-)

diff --git a/src/widget/container/backplate.rs b/src/widget/container/backplate.rs
index 0ab957d..0d26a25 100644
--- a/src/widget/container/backplate.rs
+++ b/src/widget/container/backplate.rs
@@ -171,10 +171,7 @@ impl Element for Backplate {
         let bg_color = self.color();
         let (wx, wy, ww, wh) = self.rect();
         
-        let (r1, r2, r3, r4) = self.rounded_corners();
-        let has_rounded = r1 || r2 || r3 || r4;
-        
-        if bg_color[3] != 0.0 && !has_rounded {
+        if bg_color[3] != 0.0 {
             quads.push((wx, wy, ww, wh, bg_color));
         }
         for &child_ptr in &self.children {
diff --git a/src/widget/container/breadcrumb.rs b/src/widget/container/breadcrumb.rs
index 3476180..05caff9 100644
--- a/src/widget/container/breadcrumb.rs
+++ b/src/widget/container/breadcrumb.rs
@@ -4,7 +4,7 @@ use crate::widget::display::TextLabel;
 
 #[derive(Debug, Clone)]
 pub struct Breadcrumb {
-    x: f32, y: f32, w: f32, h: f32,
+    pub base: Widget,
     hovered: bool,
     path: Vec<String>,
     hovered_seg: Option<usize>,
@@ -18,7 +18,7 @@ impl Breadcrumb {
     }
 
     pub fn new() -> Self {
-        Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false,
+        Self { base: Widget::new(), hovered: false,
                path: Vec::new(), hovered_seg: None, clicked_seg: None, network_opacity: 1.0 }
     }
 
@@ -31,7 +31,7 @@ impl Breadcrumb {
     }
 
     fn seg_at(&self, px: f32) -> Option<usize> {
-        let mut cx = self.x + BREADCRUMB_PADDING;
+        let mut cx = self.base.x + BREADCRUMB_PADDING;
         let segs = self.virtual_segs();
         for (i, seg) in segs.iter().enumerate() {
             let w = seg.len() as f32 * 7.5;
@@ -45,20 +45,10 @@ impl Breadcrumb {
 }
 
 impl Element for Breadcrumb {
-    fn as_any(&self) -> &dyn std::any::Any { self }
-    fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
-    fn as_ptr(&self) -> *mut (dyn Element + 'static) {
-        self as *const Self as *mut Self as *mut (dyn Element + 'static)
-    }
-    fn as_ptr_mut(&mut self) -> *mut (dyn Element + 'static) {
-        self as *mut Self as *mut (dyn Element + 'static)
-    }
+    crate::impl_widget_base!(Breadcrumb);
 
     fn rounded_corners(&self) -> (bool, bool, bool, bool) { (true, true, false, false) }
 
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-
     fn color(&self) -> [f32; 4] {
         let c = crate::color::breadcrumb_bg_color();
         [c[0], c[1], c[2], self.network_opacity]
@@ -103,30 +93,32 @@ impl Element for Breadcrumb {
 
     fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
         let mut quads = Vec::new();
-        quads.push((self.x, self.y, self.w, self.h, self.color()));
+        let (x, y, w, h) = self.rect();
+        quads.push((x, y, w, h, self.color()));
         if let Some(i) = self.hovered_seg {
-            let mut cx = self.x + BREADCRUMB_PADDING;
+            let mut cx = x + BREADCRUMB_PADDING;
             let segs = self.virtual_segs();
             for j in 0..i {
                 let w = segs[j].len() as f32 * 7.5;
                 cx += w + SEGMENT_GAP;
             }
             let w = segs[i].len() as f32 * 7.5;
-            quads.push((cx, self.y, w, self.h, [1.0, 1.0, 1.0, 0.06]));
+            quads.push((cx, y, w, h, [1.0, 1.0, 1.0, 0.06]));
         }
         quads
     }
 
     fn text_labels(&self) -> Vec<TextLabel> {
         let mut labels = Vec::new();
-        let mut cx = self.x + BREADCRUMB_PADDING;
+        let (x, y, _, _) = self.rect();
+        let mut cx = x + BREADCRUMB_PADDING;
         let segs = self.virtual_segs();
         let len = segs.len();
         for (i, seg) in segs.iter().enumerate() {
             labels.push(TextLabel {
                 text: seg.clone(),
                 x: cx,
-                y: self.y + 6.0,
+                y: y + 6.0,
                 font_size: 12.0,
                 color: if i == len - 1 { [0xcc, 0xcc, 0xd4] } else { [0x88, 0x88, 0x99] },
             });
@@ -144,3 +136,47 @@ impl PathController for Breadcrumb {
         self.clicked_seg.take()
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::context::UiContext;
+    use crate::widget::Element;
+
+    #[test]
+    fn test_breadcrumb_clicks() {
+        let mut breadcrumb = Breadcrumb::new();
+        breadcrumb.set_path(&["home".to_string(), "lsgalante".to_string()]);
+        // Set coordinates: x=10.0, y=20.0, w=300.0, h=24.0
+        breadcrumb.set_rect(10.0, 20.0, 300.0, 24.0);
+
+        let ctx = UiContext::new();
+
+        // Let's test hit_test
+        assert!(breadcrumb.hit_test(15.0, 25.0, &ctx));
+
+        // Let's test seg_at:
+        // x + padding = 10.0 + 8.0 = 18.0
+        // Segment 0 ("/"): length 1. width = 1 * 7.5 = 7.5. range: [18.0, 25.5)
+        // GAP = 4.0. next = 25.5 + 4.0 = 29.5
+        // Segment 1 ("home/"): length 5. width = 5 * 7.5 = 37.5. range: [29.5, 67.0)
+        // GAP = 4.0. next = 67.0 + 4.0 = 71.0
+        // Segment 2 ("lsgalante/"): length 10. width = 10 * 7.5 = 75.0. range: [71.0, 146.0)
+
+        // Click in segment 0 (/)
+        let mut ui_ctx = UiContext::new();
+        assert!(breadcrumb.mouse_input(crate::widget::MouseButton::Left, crate::widget::ElementState::Pressed, 20.0, 25.0, &mut ui_ctx));
+        assert_eq!(breadcrumb.path_click(), Some(0));
+
+        // Click in segment 1 (home/)
+        assert!(breadcrumb.mouse_input(crate::widget::MouseButton::Left, crate::widget::ElementState::Pressed, 50.0, 25.0, &mut ui_ctx));
+        assert_eq!(breadcrumb.path_click(), Some(1));
+
+        // Click in segment 2 (lsgalante/)
+        // Wait, path.len() is 2. i = 2. 2 < 2 is false.
+        // So clicking the last segment should return false.
+        assert!(!breadcrumb.mouse_input(crate::widget::MouseButton::Left, crate::widget::ElementState::Pressed, 100.0, 25.0, &mut ui_ctx));
+        assert_eq!(breadcrumb.path_click(), None);
+    }
+}
+
diff --git a/src/widget/container/mod.rs b/src/widget/container/mod.rs
index 48d24b1..e9bb372 100644
--- a/src/widget/container/mod.rs
+++ b/src/widget/container/mod.rs
@@ -30,7 +30,7 @@ pub use menu::{Menu, MenuBar};
 pub use breadcrumb::Breadcrumb;
 pub use spreadsheet::Spreadsheet;
 pub use scroll_box::ScrollBox;
-pub use scrolling_list::{ScrollingList, FontSelector};
+pub use scrolling_list::{List, FontSelector};
 pub use plate::Plate;
 pub use switcher::Switcher;
 pub use layer::Layer;
diff --git a/src/widget/container/scrolling_list.rs b/src/widget/container/scrolling_list.rs
index 942486f..1b57743 100644
--- a/src/widget/container/scrolling_list.rs
+++ b/src/widget/container/scrolling_list.rs
@@ -5,13 +5,13 @@ pub use crate::widget::input::font_selector::FontSelector;
 
 // Generic text item layout wrapper
 #[derive(Debug, Clone)]
-pub struct ScrollingList {
+pub struct List {
     pub scroll_box: ScrollBox,
     pub item_height: f32,
     pub item_gap: f32,
 }
 
-impl ScrollingList {
+impl List {
     pub fn new(item_height: f32, item_gap: f32) -> Self {
         Self {
             scroll_box: ScrollBox::new(),
@@ -41,13 +41,13 @@ impl ScrollingList {
     }
 }
 
-impl Default for ScrollingList {
+impl Default for List {
     fn default() -> Self {
         Self::new(24.0, 4.0)
     }
 }
 
-impl Element for ScrollingList {
+impl Element for List {
     fn rect(&self) -> (f32, f32, f32, f32) {
         self.scroll_box.rect()
     }
@@ -179,7 +179,7 @@ impl Element for ScrollingList {
     fn as_scroll_controller_mut(&mut self) -> Option<&mut dyn ScrollController> { Some(self) }
 }
 
-impl ScrollController for ScrollingList {
+impl ScrollController for List {
     fn update_bounds(&mut self, count: usize, viewport_y: f32, viewport_h: f32) {
         self.update_bounds(count, viewport_y, viewport_h);
     }
@@ -189,5 +189,5 @@ impl ScrollController for ScrollingList {
     }
 }
 
-unsafe impl Send for ScrollingList {}
-unsafe impl Sync for ScrollingList {}
+unsafe impl Send for List {}
+unsafe impl Sync for List {}
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 08ba79b..af691e0 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -678,7 +678,7 @@ pub use self::input::{
 };
 pub use self::container::{
     Container, ContainerLayout, OverlayLayout, VerticalLayout, GridLayout, AdaptiveGridLayout,
-    SectionContainer, Header, ContentBg, ViewportBg, ParametersBg, ScrollingList,
+    SectionContainer, Header, ContentBg, ViewportBg, ParametersBg, List,
     ScrollBox, Menu, MenuBar, Spreadsheet, Breadcrumb, Plate,
     Switcher, Layer, Page, Backplate, Paginator, ScrollBar, TreeList, TreeElement
 };