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

commitf9f463b1b3a42e79d6200783fcc393a12f912ad9
parentab80e3c9f4
authorLucas Galante <[email protected]>
date2026-07-05 02:06
Merge PageLayout and ContainerLayout traits, unify Page widget layout

 src/widget/container/container_layout.rs | 94 +++++++++++++++++++++++++++++--
 src/widget/container/mod.rs              |  2 +-
 src/widget/container/page.rs             | 97 ++------------------------------
 3 files changed, 96 insertions(+), 97 deletions(-)

diff --git a/src/widget/container/container_layout.rs b/src/widget/container/container_layout.rs
index 428a704..393606b 100644
--- a/src/widget/container/container_layout.rs
+++ b/src/widget/container/container_layout.rs
@@ -105,7 +105,7 @@ impl crate::layout::LayoutStrategy for VerticalLayout {
         self.spacing
     }
 
-    fn layout(&self, x: f32, y: f32, w: f32, _h: f32, children: &[*mut (dyn Element + 'static)], _ctx: &mut UiContext) -> f32 {
+    fn layout(&self, x: f32, y: f32, w: f32, _h: f32, children: &[*mut (dyn Element + 'static)], ctx: &mut UiContext) -> f32 {
         let left_x = x + self.padding_x;
         let available_w = (w - 2.0 * self.padding_x).max(1.0);
         let mut current_y = y + self.padding_y;
@@ -115,7 +115,11 @@ impl crate::layout::LayoutStrategy for VerticalLayout {
                 let child = &mut *child_ptr;
                 let ch = child.preferred_height().unwrap_or(child.rect().3);
                 let use_h = if ch > 0.0 { ch } else { 44.0 };
-                child.set_rect(left_x, current_y, available_w, use_h);
+                child.layout(
+                    Point { x: left_x, y: current_y },
+                    LayoutConstraints::new(available_w, available_w, use_h, use_h),
+                    ctx,
+                );
                 current_y += use_h + self.spacing;
             }
         }
@@ -199,7 +203,7 @@ impl crate::layout::LayoutStrategy for GridLayout {
         self.gap
     }
 
-    fn layout(&self, x: f32, y: f32, w: f32, _h: f32, children: &[*mut (dyn Element + 'static)], _ctx: &mut UiContext) -> f32 {
+    fn layout(&self, x: f32, y: f32, w: f32, _h: f32, children: &[*mut (dyn Element + 'static)], ctx: &mut UiContext) -> f32 {
         let count = children.len();
         if count == 0 {
             return 0.0;
@@ -228,7 +232,11 @@ impl crate::layout::LayoutStrategy for GridLayout {
                 
                 let cx = x + self.padding_x + min_col as f32 * (col_w + self.gap);
                 let cy = col_heights[min_col];
-                child.set_rect(cx, cy, col_w, use_h);
+                child.layout(
+                    Point { x: cx, y: cy },
+                    LayoutConstraints::new(col_w, col_w, use_h, use_h),
+                    ctx,
+                );
                 col_heights[min_col] += use_h + self.gap;
             }
         }
@@ -359,3 +367,81 @@ impl ContainerLayout for AdaptiveGridLayout {
         Box::new(self.clone())
     }
 }
+
+#[derive(Debug, Clone, Copy)]
+pub struct ColumnsLayout {
+    pub padding_x: f32,
+    pub padding_y: f32,
+    pub spacing: f32,
+}
+
+impl Default for ColumnsLayout {
+    fn default() -> Self {
+        Self {
+            padding_x: 8.0,
+            padding_y: 10.0,
+            spacing: 12.0,
+        }
+    }
+}
+
+impl crate::layout::LayoutStrategy for ColumnsLayout {
+    fn init(&mut self, _left: f32, _top: f32, _width: f32, _height: f32) {}
+    fn allocate(&mut self, ww: f32, wh: f32) -> (f32, f32, f32, f32) { (0.0, 0.0, ww, wh) }
+    fn get_gap(&self) -> f32 { self.spacing }
+
+    fn layout(&self, x: f32, y: f32, w: f32, h: f32, children: &[*mut (dyn Element + 'static)], ctx: &mut UiContext) -> f32 {
+        let count = children.len();
+        if count == 0 {
+            return 0.0;
+        }
+        let total_spacing = self.spacing * (count - 1) as f32;
+        let total_padding = self.padding_x * 2.0;
+        let available_w = (w - total_padding - total_spacing).max(1.0);
+        let col_w = available_w / count as f32;
+        let use_h = (h - 2.0 * self.padding_y).max(1.0);
+        let start_y = y + self.padding_y;
+
+        let mut current_x = x + self.padding_x;
+        for &child_ptr in children {
+            unsafe {
+                let child = &mut *child_ptr;
+                child.layout(
+                    Point { x: current_x, y: start_y },
+                    LayoutConstraints::new(col_w, col_w, use_h, use_h),
+                    ctx,
+                );
+                current_x += col_w + self.spacing;
+            }
+        }
+        use_h + 2.0 * self.padding_y
+    }
+
+    fn measure(&self, constraints: LayoutConstraints, children: &[*mut (dyn Element + 'static)], ctx: &UiContext) -> Size {
+        let count = children.len();
+        if count == 0 {
+            return Size { width: constraints.min_width, height: constraints.min_height };
+        }
+        let mut max_h = 0.0f32;
+        for &child_ptr in children {
+            unsafe {
+                let size = (*child_ptr).measure(constraints, ctx);
+                max_h = max_h.max(size.height);
+            }
+        }
+        Size {
+            width: constraints.max_width,
+            height: (max_h + 2.0 * self.padding_y).clamp(constraints.min_height, constraints.max_height),
+        }
+    }
+
+    fn box_clone(&self) -> Box<dyn crate::layout::LayoutStrategy> {
+        Box::new(*self)
+    }
+}
+
+impl ContainerLayout for ColumnsLayout {
+    fn box_clone_container(&self) -> Box<dyn ContainerLayout> {
+        Box::new(*self)
+    }
+}
diff --git a/src/widget/container/mod.rs b/src/widget/container/mod.rs
index 025a482..a2b6ffd 100644
--- a/src/widget/container/mod.rs
+++ b/src/widget/container/mod.rs
@@ -24,7 +24,7 @@ pub mod hbox;
 
 pub use container::Container;
 pub use control_panel::ControlPanel;
-pub use container_layout::{ContainerLayout, OverlayLayout, VerticalLayout, GridLayout, AdaptiveGridLayout};
+pub use container_layout::{ContainerLayout, OverlayLayout, VerticalLayout, GridLayout, AdaptiveGridLayout, ColumnsLayout};
 pub use section_container::SectionContainer;
 pub use header::Header;
 pub use content_bg::ContentBg;
diff --git a/src/widget/container/page.rs b/src/widget/container/page.rs
index 3996ae2..650f608 100644
--- a/src/widget/container/page.rs
+++ b/src/widget/container/page.rs
@@ -3,100 +3,13 @@ use crate::context::UiContext;
 use crate::widget::display::TextLabel;
 use super::layer::Layer;
 
-pub trait PageLayout {
-    fn layout(&self, x: f32, y: f32, w: f32, h: f32, children: &[*mut (dyn Element + 'static)], ctx: &mut UiContext) -> f32;
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct VerticalLayout {
-    pub padding_x: f32,
-    pub padding_y: f32,
-    pub spacing: f32,
-}
-
-impl Default for VerticalLayout {
-    fn default() -> Self {
-        Self {
-            padding_x: 8.0,
-            padding_y: 10.0,
-            spacing: 8.0,
-        }
-    }
-}
-
-impl PageLayout for VerticalLayout {
-    fn layout(&self, x: f32, y: f32, w: f32, _h: f32, children: &[*mut (dyn Element + 'static)], ctx: &mut UiContext) -> f32 {
-        let padding_x = self.padding_x;
-        let padding_y = self.padding_y;
-        let left_x = x + padding_x;
-        let available_w = (w - 2.0 * padding_x).max(1.0);
-        let mut current_y = y + padding_y;
-        let spacing = self.spacing;
-
-        for &child_ptr in children {
-            let child = unsafe { &mut *child_ptr };
-            let (_, _, _, ch) = child.rect();
-            let use_h = if ch > 0.0 { ch } else { 42.0 };
-            child.layout(
-                Point { x: left_x, y: current_y },
-                LayoutConstraints::new(available_w, available_w, use_h, use_h),
-                ctx,
-            );
-            current_y += use_h + spacing;
-        }
-        current_y - y
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct ColumnsLayout {
-    pub padding_x: f32,
-    pub padding_y: f32,
-    pub spacing: f32,
-}
-
-impl Default for ColumnsLayout {
-    fn default() -> Self {
-        Self {
-            padding_x: 8.0,
-            padding_y: 10.0,
-            spacing: 12.0,
-        }
-    }
-}
-
-impl PageLayout for ColumnsLayout {
-    fn layout(&self, x: f32, y: f32, w: f32, h: f32, children: &[*mut (dyn Element + 'static)], ctx: &mut UiContext) -> f32 {
-        let count = children.len();
-        if count == 0 {
-            return 0.0;
-        }
-        let total_spacing = self.spacing * (count - 1) as f32;
-        let total_padding = self.padding_x * 2.0;
-        let available_w = (w - total_padding - total_spacing).max(1.0);
-        let col_w = available_w / count as f32;
-        let use_h = (h - 2.0 * self.padding_y).max(1.0);
-        let start_y = y + self.padding_y;
-
-        let mut current_x = x + self.padding_x;
-        for &child_ptr in children {
-            let child = unsafe { &mut *child_ptr };
-            child.layout(
-                Point { x: current_x, y: start_y },
-                LayoutConstraints::new(col_w, col_w, use_h, use_h),
-                ctx,
-            );
-            current_x += col_w + self.spacing;
-        }
-        use_h + 2.0 * self.padding_y
-    }
-}
+use super::container_layout::ContainerLayout;
 
 pub struct Page {
     pub base: Layer,
     pub visible: bool,
     pub owned_children: Vec<Box<dyn Element>>,
-    pub layout: Box<dyn PageLayout>,
+    pub layout: Box<dyn ContainerLayout>,
     pub scroll_y: f32,
     pub content_h: f32,
     pub scroll_bar: ScrollBar,
@@ -119,15 +32,15 @@ impl Page {
             base: Layer::new(x, y, w, h),
             visible: true,
             owned_children: Vec::new(),
-            layout: Box::new(VerticalLayout::default()),
+            layout: Box::new(super::container_layout::VerticalLayout::default()),
             scroll_y: 0.0,
             content_h: 0.0,
             scroll_bar: ScrollBar::new(),
         }
     }
 
-    pub fn with_layout(mut self, layout: Box<dyn PageLayout>) -> Self {
-        self.layout = layout;
+    pub fn with_layout<L: ContainerLayout + 'static>(mut self, layout: L) -> Self {
+        self.layout = Box::new(layout);
         self
     }