1use crate::core::Rect;
4use crate::layout::{DesiredSize, Layout, SizeConstraints};
5
6#[derive(Debug, Clone, Copy)]
8pub struct GridSpan {
9 pub col_span: usize,
10 pub row_span: usize,
11}
12
13impl Default for GridSpan {
14 fn default() -> Self {
15 Self {
16 col_span: 1,
17 row_span: 1,
18 }
19 }
20}
21
22#[derive(Debug, Clone)]
24pub struct GridItem {
25 pub index: usize,
26 pub span: GridSpan,
27}
28
29impl GridItem {
30 pub fn new(index: usize) -> Self {
31 Self {
32 index,
33 span: GridSpan::default(),
34 }
35 }
36
37 pub fn col_span(mut self, span: usize) -> Self {
38 self.span.col_span = span;
39 self
40 }
41
42 pub fn row_span(mut self, span: usize) -> Self {
43 self.span.row_span = span;
44 self
45 }
46}
47
48pub struct GridLayout {
50 columns: usize,
51 rows: usize,
52 gap: f32,
53 items: Vec<GridItem>,
54}
55
56impl GridLayout {
57 pub fn new(columns: usize, rows: usize) -> Self {
58 Self {
59 columns: columns.max(1),
60 rows: rows.max(1),
61 gap: 0.0,
62 items: Vec::new(),
63 }
64 }
65
66 pub fn gap(mut self, gap: f32) -> Self {
67 self.gap = gap;
68 self
69 }
70
71 pub fn item(mut self, item: GridItem) -> Self {
72 self.items.push(item);
73 self
74 }
75}
76
77impl Layout for GridLayout {
78 fn measure(&self, children: &[DesiredSize], constraints: SizeConstraints) -> DesiredSize {
79 if children.is_empty() {
80 return DesiredSize::zero();
81 }
82 let max_child_w = children.iter().map(|c| c.width).fold(0.0f32, f32::max);
83 let max_child_h = children.iter().map(|c| c.height).fold(0.0f32, f32::max);
84
85 let total_w =
86 max_child_w * self.columns as f32 + self.gap * (self.columns as f32 - 1.0).max(0.0);
87 let total_h = max_child_h * self.rows as f32 + self.gap * (self.rows as f32 - 1.0).max(0.0);
88
89 let (w, h) = constraints.clamp(total_w, total_h);
90 DesiredSize::new(w, h)
91 }
92
93 fn arrange(&self, children: &[DesiredSize], area: Rect) -> Vec<Rect> {
94 if children.is_empty() {
95 return vec![];
96 }
97
98 let col_w =
99 (area.width - self.gap * (self.columns as f32 - 1.0).max(0.0)) / self.columns as f32;
100 let row_h = (area.height - self.gap * (self.rows as f32 - 1.0).max(0.0)) / self.rows as f32;
101
102 if !self.items.is_empty() {
104 return self.arrange_with_items(children, area, col_w, row_h);
105 }
106
107 let mut rects = Vec::with_capacity(children.len());
109 for (i, _child) in children.iter().enumerate() {
110 let col = i % self.columns;
111 let row = i / self.columns;
112 let x = area.x + col as f32 * (col_w + self.gap);
113 let y = area.y + row as f32 * (row_h + self.gap);
114 rects.push(Rect::new(x, y, col_w, row_h));
115 }
116 rects
117 }
118}
119
120impl GridLayout {
121 fn arrange_with_items(
122 &self,
123 children: &[DesiredSize],
124 area: Rect,
125 col_w: f32,
126 row_h: f32,
127 ) -> Vec<Rect> {
128 let mut rects = Vec::with_capacity(children.len());
129 let mut col = 0usize;
130 let mut row = 0usize;
131
132 for (i, _child) in children.iter().enumerate() {
133 let span = self
134 .items
135 .iter()
136 .find(|it| it.index == i)
137 .map(|it| it.span)
138 .unwrap_or_default();
139
140 let cs = span.col_span.min(self.columns - col);
141 let rs = span.row_span.min(self.rows - row);
142
143 let x = area.x + col as f32 * (col_w + self.gap);
144 let y = area.y + row as f32 * (row_h + self.gap);
145 let w = col_w * cs as f32 + self.gap * (cs as f32 - 1.0).max(0.0);
146 let h = row_h * rs as f32 + self.gap * (rs as f32 - 1.0).max(0.0);
147
148 rects.push(Rect::new(x, y, w, h));
149
150 col += cs;
151 if col >= self.columns {
152 col = 0;
153 row += 1;
154 }
155 }
156 rects
157 }
158}