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

commitb72318875ff8687b4f4cf4da8ffb400ae6920c51
parent4cd304f7eb
authorLucas Galante <[email protected]>
date2026-06-15 10:26
Support dynamic PageLayout strategies on Page container widget

 src/widget/container/page.rs | 101 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 87 insertions(+), 14 deletions(-)

diff --git a/src/widget/container/page.rs b/src/widget/container/page.rs
index 2b70760..a64ef10 100644
--- a/src/widget/container/page.rs
+++ b/src/widget/container/page.rs
@@ -3,10 +3,90 @@ 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)]);
+}
+
+#[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)]) {
+        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.set_rect(left_x, current_y, available_w, use_h);
+            current_y += use_h + spacing;
+        }
+    }
+}
+
+#[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)]) {
+        let count = children.len();
+        if count == 0 {
+            return;
+        }
+        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.set_rect(current_x, start_y, col_w, use_h);
+            current_x += col_w + self.spacing;
+        }
+    }
+}
+
 pub struct Page {
     pub base: Layer,
     pub visible: bool,
     pub owned_children: Vec<Box<dyn Element>>,
+    pub layout: Box<dyn PageLayout>,
 }
 
 impl std::fmt::Debug for Page {
@@ -24,9 +104,15 @@ impl Page {
             base: Layer::new(x, y, w, h),
             visible: true,
             owned_children: Vec::new(),
+            layout: Box::new(VerticalLayout::default()),
         }
     }
 
+    pub fn with_layout(mut self, layout: Box<dyn PageLayout>) -> Self {
+        self.layout = layout;
+        self
+    }
+
     pub fn add_child_owned(&mut self, child: Box<dyn Element>, ctx: &mut UiContext) {
         let ptr = &*child as *const (dyn Element + 'static) as *mut (dyn Element + 'static);
         self.owned_children.push(child);
@@ -52,20 +138,7 @@ impl Element for Page {
         if !self.visible {
             return;
         }
-        let padding_x = 8.0;
-        let padding_y = 10.0;
-        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 = 8.0;
-
-        for &child_ptr in &self.base.children {
-            let child = unsafe { &mut *child_ptr };
-            let (_, _, _, ch) = child.rect();
-            let use_h = if ch > 0.0 { ch } else { 42.0 };
-            child.set_rect(left_x, current_y, available_w, use_h);
-            current_y += use_h + spacing;
-        }
+        self.layout.layout(x, y, w, h, &self.base.children);
     }
 
     fn color(&self) -> [f32; 4] {