1use crate::core::layout::{self, Layout};
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::widget::{Meta, Operation, Tree};
7use crate::core::{Event, Length, Pixels, Rectangle, Shell, Size, Vector, Widget};
8
9pub struct Grid<W> {
11 spacing: f32,
12 columns: Constraint,
13 width: Option<Pixels>,
14 height: Sizing,
15 children: Vec<W>,
16}
17
18enum Constraint {
19 MaxWidth(Pixels),
20 Amount(usize),
21}
22
23impl<W> Grid<W> {
24 pub fn new() -> Self {
26 Self::from_vec(Vec::new())
27 }
28
29 pub fn with_capacity(capacity: usize) -> Self {
31 Self::from_vec(Vec::with_capacity(capacity))
32 }
33
34 pub fn with_children(children: impl IntoIterator<Item = W>) -> Self
36 where
37 W: Meta,
38 {
39 let iterator = children.into_iter();
40
41 Self::with_capacity(iterator.size_hint().0).extend(iterator)
42 }
43
44 pub fn from_vec(children: Vec<W>) -> Self {
46 Self {
47 spacing: 0.0,
48 columns: Constraint::Amount(3),
49 width: None,
50 height: Sizing::AspectRatio(1.0),
51 children,
52 }
53 }
54
55 pub fn spacing(mut self, amount: impl Into<Pixels>) -> Self {
57 self.spacing = amount.into().0;
58 self
59 }
60
61 pub fn width(mut self, width: impl Into<Pixels>) -> Self {
67 self.width = Some(width.into());
68 self
69 }
70
71 pub fn height(mut self, height: impl Into<Sizing>) -> Self {
75 self.height = height.into();
76 self
77 }
78
79 pub fn columns(mut self, column: usize) -> Self {
81 self.columns = Constraint::Amount(column);
82 self
83 }
84
85 pub fn fluid(mut self, max_width: impl Into<Pixels>) -> Self {
88 self.columns = Constraint::MaxWidth(max_width.into());
89 self
90 }
91
92 pub fn push(mut self, child: W) -> Self
94 where
95 W: Meta,
96 {
97 self.children.push(child);
98 self
99 }
100
101 pub fn push_maybe(self, child: Option<W>) -> Self
103 where
104 W: Meta,
105 {
106 if let Some(child) = child {
107 self.push(child)
108 } else {
109 self
110 }
111 }
112
113 pub fn extend(self, children: impl IntoIterator<Item = W>) -> Self
115 where
116 W: Meta,
117 {
118 children.into_iter().fold(self, Self::push)
119 }
120}
121
122impl<W> Default for Grid<W> {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128impl<W> FromIterator<W> for Grid<W>
129where
130 W: Meta,
131{
132 fn from_iter<T: IntoIterator<Item = W>>(iter: T) -> Self {
133 Self::with_children(iter)
134 }
135}
136
137impl<W> Meta for Grid<W> {}
138
139impl<W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Grid<W>
140where
141 W: Widget<Message, Theme, Renderer>,
142{
143 fn diff(&mut self, tree: &mut Tree) {
144 tree.diff_children(&mut self.children);
145 }
146
147 fn size(&self) -> Size<Length> {
148 Size {
149 width: self
150 .width
151 .map(|pixels| Length::Fixed(pixels.0))
152 .unwrap_or(Length::Fill),
153 height: match self.height {
154 Sizing::AspectRatio(_) => Length::Shrink,
155 Sizing::EvenlyDistribute(length) => length,
156 },
157 }
158 }
159
160 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
161 let size = self.size();
162 let limits = limits.width(size.width).height(size.height);
163 let available = limits.max;
164
165 if limits.compression.width && self.width.is_none() {
166 tree.size = Size::ZERO;
167 return;
168 }
169
170 let cells_per_row = match self.columns {
171 Constraint::MaxWidth(pixels) => {
173 ((available.width + self.spacing) / (pixels.0 + self.spacing)).ceil() as usize
174 }
175 Constraint::Amount(amount) => amount,
176 };
177
178 if self.children.is_empty() || cells_per_row == 0 {
179 tree.size = limits.resolve(size.width, size.height, Size::ZERO);
180 return;
181 }
182
183 let cell_width =
184 (available.width - self.spacing * (cells_per_row - 1) as f32) / cells_per_row as f32;
185
186 let cell_height = match self.height {
187 Sizing::AspectRatio(ratio) => Some(cell_width / ratio),
188 Sizing::EvenlyDistribute(Length::Shrink) => None,
189 Sizing::EvenlyDistribute(_) => {
190 let total_rows = self.children.len().div_ceil(cells_per_row);
191 Some(
192 (available.height - self.spacing * (total_rows - 1) as f32) / total_rows as f32,
193 )
194 }
195 };
196
197 let cell_limits = layout::Limits::new(
198 Size::new(cell_width, cell_height.unwrap_or(0.0)),
199 Size::new(cell_width, cell_height.unwrap_or(available.height)),
200 );
201
202 let mut x = 0.0;
203 let mut y = 0.0;
204 let mut row_height = 0.0f32;
205
206 for (i, (child, tree)) in self.children.iter_mut().zip(&mut tree.children).enumerate() {
207 child.layout(tree, renderer, &cell_limits);
208
209 let size = tree.size;
210
211 tree.translation = Vector::new(x, y);
212
213 x += size.width + self.spacing;
214 row_height = row_height.max(size.height);
215
216 if (i + 1) % cells_per_row == 0 {
217 y += cell_height.unwrap_or(row_height) + self.spacing;
218 x = 0.0;
219 row_height = 0.0;
220 }
221 }
222
223 if x == 0.0 {
224 y -= self.spacing;
225 } else {
226 y += cell_height.unwrap_or(row_height);
227 }
228
229 tree.size = Size::new(available.width, y);
230 }
231
232 fn operate(
233 &mut self,
234 tree: &mut Tree,
235 layout: Layout,
236 viewport: &Rectangle,
237 renderer: &Renderer,
238 operation: &mut dyn Operation,
239 ) {
240 operation.container(None, layout.bounds(), viewport);
241 operation.traverse(&mut |operation| {
242 self.children
243 .iter_mut()
244 .zip(layout.iter_mut(&mut tree.children))
245 .for_each(|(child, (layout, state))| {
246 child.operate(state, layout, viewport, renderer, operation);
247 });
248 });
249 }
250
251 fn update(
252 &mut self,
253 tree: &mut Tree,
254 event: &Event,
255 layout: Layout,
256 cursor: mouse::Cursor,
257 renderer: &Renderer,
258 shell: &mut Shell<'_, Message>,
259 viewport: &Rectangle,
260 ) {
261 for (child, (layout, tree)) in self
262 .children
263 .iter_mut()
264 .zip(layout.iter_mut(&mut tree.children))
265 {
266 child.update(tree, event, layout, cursor, renderer, shell, viewport);
267 }
268 }
269
270 fn mouse_interaction(
271 &self,
272 tree: &Tree,
273 layout: Layout,
274 cursor: mouse::Cursor,
275 viewport: &Rectangle,
276 renderer: &Renderer,
277 ) -> mouse::Interaction {
278 self.children
279 .iter()
280 .zip(layout.iter(&tree.children))
281 .map(|(child, (layout, tree))| {
282 child.mouse_interaction(tree, layout, cursor, viewport, renderer)
283 })
284 .max()
285 .unwrap_or_default()
286 }
287
288 fn draw(
289 &self,
290 tree: &Tree,
291 renderer: &mut Renderer,
292 theme: &Theme,
293 style: &renderer::Style,
294 layout: Layout,
295 cursor: mouse::Cursor,
296 viewport: &Rectangle,
297 ) {
298 if let Some(viewport) = layout.bounds().intersection(viewport) {
299 for (child, (layout, tree)) in self
300 .children
301 .iter()
302 .zip(layout.iter(&tree.children))
303 .filter(|(_, (layout, _))| layout.bounds().intersects(&viewport))
304 {
305 child.draw(tree, renderer, theme, style, layout, cursor, &viewport);
306 }
307 }
308 }
309
310 fn overlay<'b>(
311 &'b mut self,
312 tree: &'b mut Tree,
313 layout: Layout,
314 renderer: &Renderer,
315 viewport: &Rectangle,
316 translation: Vector,
317 window: Size,
318 ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
319 overlay::from_children(
320 &mut self.children,
321 tree,
322 layout,
323 renderer,
324 viewport,
325 translation,
326 window,
327 )
328 }
329}
330
331#[derive(Debug, Clone, Copy, PartialEq)]
333pub enum Sizing {
334 AspectRatio(f32),
340
341 EvenlyDistribute(Length),
344}
345
346impl From<f32> for Sizing {
347 fn from(height: f32) -> Self {
348 Self::EvenlyDistribute(Length::from(height))
349 }
350}
351
352impl From<Length> for Sizing {
353 fn from(height: Length) -> Self {
354 Self::EvenlyDistribute(height)
355 }
356}
357
358pub fn aspect_ratio(width: impl Into<Pixels>, height: impl Into<Pixels>) -> Sizing {
360 Sizing::AspectRatio(width.into().0 / height.into().0)
361}