1use crate::{Element, Renderer};
5use derive_setters::Setters;
6use iced_core::event::{self, Event};
7use iced_core::widget::{Operation, Tree};
8use iced_core::{
9 Alignment, Clipboard, Layout, Length, Padding, Rectangle, Shell, Vector, Widget, layout, mouse,
10 overlay, renderer,
11};
12
13#[must_use]
15#[derive(Setters)]
16pub struct Grid<'a, Message> {
17 #[setters(skip)]
18 children: Vec<Element<'a, Message>>,
19 #[setters(skip)]
21 assignments: Vec<Assignment>,
22 padding: Padding,
24 column_alignment: Alignment,
26 row_alignment: Alignment,
28 #[setters(into, strip_option)]
30 justify_content: Option<crate::widget::JustifyContent>,
31 column_spacing: u16,
33 row_spacing: u16,
35 width: Length,
37 height: Length,
39 max_width: f32,
41 #[setters(skip)]
42 column: u16,
43 #[setters(skip)]
44 row: u16,
45}
46
47impl<Message> Default for Grid<'_, Message> {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl<'a, Message> Grid<'a, Message> {
54 pub const fn new() -> Self {
55 Self {
56 children: Vec::new(),
57 assignments: Vec::new(),
58 padding: Padding::ZERO,
59 column_alignment: Alignment::Start,
60 row_alignment: Alignment::Start,
61 justify_content: None,
62 column_spacing: 4,
63 row_spacing: 4,
64 width: Length::Shrink,
65 height: Length::Shrink,
66 max_width: f32::INFINITY,
67 column: 1,
68 row: 1,
69 }
70 }
71
72 pub fn push(mut self, widget: impl Into<Element<'a, Message>>) -> Self {
74 self.children.push(widget.into());
75
76 self.assignments.push(Assignment {
77 column: self.column,
78 row: self.row,
79 width: 1,
80 height: 1,
81 });
82
83 self.column += 1;
84
85 self
86 }
87
88 pub fn push_with<W, S>(mut self, widget: W, setup: S) -> Self
90 where
91 W: Into<Element<'a, Message>>,
92 S: Fn(Assignment) -> Assignment,
93 {
94 self.children.push(widget.into());
95
96 self.assignments.push(setup(Assignment {
97 column: self.column,
98 row: self.row,
99 width: 1,
100 height: 1,
101 }));
102
103 self.column += 1;
104
105 self
106 }
107
108 #[inline]
109 pub fn insert_row(mut self) -> Self {
110 self.row += 1;
111 self.column = 1;
112 self
113 }
114}
115
116impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for Grid<'_, Message> {
117 fn children(&self) -> Vec<Tree> {
118 self.children.iter().map(Tree::new).collect()
119 }
120
121 fn diff(&mut self, tree: &mut Tree) {
122 tree.diff_children(self.children.as_mut_slice());
123 }
124
125 fn size(&self) -> iced_core::Size<Length> {
126 iced_core::Size::new(self.width, self.height)
127 }
128
129 fn layout(
130 &self,
131 tree: &mut Tree,
132 renderer: &Renderer,
133 limits: &layout::Limits,
134 ) -> layout::Node {
135 let size = self.size();
136 let limits = limits
137 .max_width(self.max_width)
138 .width(size.width)
139 .height(size.height);
140
141 super::layout::resolve(
142 renderer,
143 &limits,
144 &self.children,
145 &self.assignments,
146 self.width,
147 self.height,
148 self.padding,
149 self.column_alignment,
150 self.row_alignment,
151 self.justify_content,
152 f32::from(self.column_spacing),
153 f32::from(self.row_spacing),
154 &mut tree.children,
155 )
156 }
157
158 fn operate(
159 &self,
160 tree: &mut Tree,
161 layout: Layout<'_>,
162 renderer: &Renderer,
163 operation: &mut dyn Operation<()>,
164 ) {
165 operation.container(None, layout.bounds(), &mut |operation| {
166 self.children
167 .iter()
168 .zip(&mut tree.children)
169 .zip(layout.children())
170 .for_each(|((child, state), c_layout)| {
171 child.as_widget().operate(
172 state,
173 c_layout.with_virtual_offset(layout.virtual_offset()),
174 renderer,
175 operation,
176 );
177 });
178 });
179 }
180
181 fn on_event(
182 &mut self,
183 tree: &mut Tree,
184 event: Event,
185 layout: Layout<'_>,
186 cursor: mouse::Cursor,
187 renderer: &Renderer,
188 clipboard: &mut dyn Clipboard,
189 shell: &mut Shell<'_, Message>,
190 viewport: &Rectangle,
191 ) -> event::Status {
192 self.children
193 .iter_mut()
194 .zip(&mut tree.children)
195 .zip(layout.children())
196 .map(|((child, state), c_layout)| {
197 child.as_widget_mut().on_event(
198 state,
199 event.clone(),
200 c_layout.with_virtual_offset(layout.virtual_offset()),
201 cursor,
202 renderer,
203 clipboard,
204 shell,
205 viewport,
206 )
207 })
208 .fold(event::Status::Ignored, event::Status::merge)
209 }
210
211 fn mouse_interaction(
212 &self,
213 tree: &Tree,
214 layout: Layout<'_>,
215 cursor: mouse::Cursor,
216 viewport: &Rectangle,
217 renderer: &Renderer,
218 ) -> mouse::Interaction {
219 self.children
220 .iter()
221 .zip(&tree.children)
222 .zip(layout.children())
223 .map(|((child, state), c_layout)| {
224 child.as_widget().mouse_interaction(
225 state,
226 c_layout.with_virtual_offset(layout.virtual_offset()),
227 cursor,
228 viewport,
229 renderer,
230 )
231 })
232 .max()
233 .unwrap_or_default()
234 }
235
236 fn draw(
237 &self,
238 tree: &Tree,
239 renderer: &mut Renderer,
240 theme: &crate::Theme,
241 style: &renderer::Style,
242 layout: Layout<'_>,
243 cursor: mouse::Cursor,
244 viewport: &Rectangle,
245 ) {
246 for ((child, state), c_layout) in self
247 .children
248 .iter()
249 .zip(&tree.children)
250 .zip(layout.children())
251 {
252 child.as_widget().draw(
253 state,
254 renderer,
255 theme,
256 style,
257 c_layout.with_virtual_offset(layout.virtual_offset()),
258 cursor,
259 viewport,
260 );
261 }
262 }
263
264 fn overlay<'b>(
265 &'b mut self,
266 tree: &'b mut Tree,
267 layout: Layout<'_>,
268 renderer: &Renderer,
269 translation: Vector,
270 ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
271 overlay::from_children(&mut self.children, tree, layout, renderer, translation)
272 }
273
274 #[cfg(feature = "a11y")]
275 fn a11y_nodes(
277 &self,
278 layout: Layout<'_>,
279 state: &Tree,
280 p: mouse::Cursor,
281 ) -> iced_accessibility::A11yTree {
282 use iced_accessibility::A11yTree;
283 A11yTree::join(
284 self.children
285 .iter()
286 .zip(layout.children())
287 .zip(state.children.iter())
288 .map(|((c, c_layout), state)| {
289 c.as_widget().a11y_nodes(
290 c_layout.with_virtual_offset(layout.virtual_offset()),
291 state,
292 p,
293 )
294 }),
295 )
296 }
297
298 fn drag_destinations(
299 &self,
300 state: &Tree,
301 layout: Layout<'_>,
302 renderer: &Renderer,
303 dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
304 ) {
305 for ((e, c_layout), state) in self
306 .children
307 .iter()
308 .zip(layout.children())
309 .zip(state.children.iter())
310 {
311 e.as_widget().drag_destinations(
312 state,
313 c_layout.with_virtual_offset(layout.virtual_offset()),
314 renderer,
315 dnd_rectangles,
316 );
317 }
318 }
319}
320
321impl<'a, Message: 'static + Clone> From<Grid<'a, Message>> for Element<'a, Message> {
322 fn from(flex_row: Grid<'a, Message>) -> Self {
323 Self::new(flex_row)
324 }
325}
326
327#[derive(Copy, Clone, Debug, Setters)]
328#[must_use]
329pub struct Assignment {
330 pub(super) column: u16,
331 pub(super) row: u16,
332 pub(super) width: u16,
333 pub(super) height: u16,
334}
335
336impl Default for Assignment {
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342impl Assignment {
343 pub const fn new() -> Self {
344 Self {
345 column: 0,
346 row: 0,
347 width: 1,
348 height: 1,
349 }
350 }
351}
352
353impl From<(u16, u16)> for Assignment {
354 fn from((column, row): (u16, u16)) -> Self {
355 Self {
356 column,
357 row,
358 width: 1,
359 height: 1,
360 }
361 }
362}
363
364impl From<(u16, u16, u16, u16)> for Assignment {
365 fn from((column, row, width, height): (u16, u16, u16, u16)) -> Self {
366 Self {
367 column,
368 row,
369 width,
370 height,
371 }
372 }
373}