1use iced_core::{
5 Alignment, Clipboard, Element, Event, Layout, Length, Padding, Pixels, Point, Rectangle, Shell,
6 Size, Vector, Widget,
7 layout::{Limits, Node},
8 mouse::{self, Cursor},
9 overlay, renderer,
10 widget::{Operation, Tree},
11};
12use std::marker::PhantomData;
13
14#[allow(missing_debug_implementations)]
16pub struct Wrap<
17 'a,
18 Message,
19 Direction,
20 Theme = iced_widget::Theme,
21 Renderer = iced_widget::Renderer,
22> {
23 pub elements: Vec<Element<'a, Message, Theme, Renderer>>,
25 pub alignment: Alignment,
27 pub width: Length,
29 pub height: Length,
31 pub max_width: f32,
33 pub max_height: f32,
35 pub padding: Padding,
37 pub spacing: Pixels,
39 pub line_spacing: Pixels,
41 pub line_minimal_length: f32,
43 #[allow(clippy::missing_docs_in_private_items)]
44 _direction: PhantomData<Direction>,
45}
46
47impl<'a, Message, Theme, Renderer> Wrap<'a, Message, direction::Horizontal, Theme, Renderer> {
48 #[must_use]
50 pub fn new() -> Self {
51 Self::with_elements(Vec::new())
52 }
53
54 #[must_use]
59 pub fn with_elements(elements: Vec<Element<'a, Message, Theme, Renderer>>) -> Self {
60 Self {
61 elements,
62 ..Wrap::default()
63 }
64 }
65}
66
67impl<'a, Message, Theme, Renderer> Wrap<'a, Message, direction::Vertical, Theme, Renderer> {
68 #[must_use]
70 pub fn new_vertical() -> Self {
71 Self::with_elements_vertical(Vec::new())
72 }
73
74 #[must_use]
79 pub fn with_elements_vertical(elements: Vec<Element<'a, Message, Theme, Renderer>>) -> Self {
80 Self {
81 elements,
82 ..Wrap::default()
83 }
84 }
85}
86
87impl<'a, Message, Renderer, Direction, Theme> Wrap<'a, Message, Direction, Theme, Renderer> {
88 #[must_use]
90 pub fn spacing(mut self, spacing: impl Into<Pixels>) -> Self {
91 self.spacing = spacing.into();
92 self
93 }
94
95 #[must_use]
97 pub fn line_spacing(mut self, spacing: impl Into<Pixels>) -> Self {
98 self.line_spacing = spacing.into();
99 self
100 }
101
102 #[must_use]
104 pub const fn line_minimal_length(mut self, units: f32) -> Self {
105 self.line_minimal_length = units;
106 self
107 }
108
109 #[must_use]
111 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
112 self.padding = padding.into();
113 self
114 }
115
116 #[must_use]
118 pub const fn width_items(mut self, width: Length) -> Self {
119 self.width = width;
120 self
121 }
122
123 #[must_use]
125 pub const fn height_items(mut self, height: Length) -> Self {
126 self.height = height;
127 self
128 }
129
130 #[must_use]
132 pub const fn max_width(mut self, max_width: f32) -> Self {
133 self.max_width = max_width;
134 self
135 }
136
137 #[must_use]
139 pub const fn max_height(mut self, max_height: f32) -> Self {
140 self.max_height = max_height;
141 self
142 }
143
144 #[must_use]
146 pub const fn align_items(mut self, align: Alignment) -> Self {
147 self.alignment = align;
148 self
149 }
150
151 #[must_use]
153 pub fn push<E>(mut self, element: E) -> Self
154 where
155 E: Into<Element<'a, Message, Theme, Renderer>>,
156 {
157 self.elements.push(element.into());
158 self
159 }
160}
161
162impl<Message, Renderer, Direction, Theme> Widget<Message, Theme, Renderer>
163 for Wrap<'_, Message, Direction, Theme, Renderer>
164where
165 Self: WrapLayout<Renderer>,
166 Renderer: renderer::Renderer,
167{
168 fn children(&self) -> Vec<Tree> {
169 self.elements.iter().map(Tree::new).collect()
170 }
171
172 fn diff(&self, tree: &mut Tree) {
173 tree.diff_children(&self.elements);
174 }
175
176 fn size(&self) -> Size<Length> {
177 Size::new(self.width, self.height)
178 }
179
180 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
181 self.inner_layout(tree, renderer, limits)
182 }
183
184 fn update(
185 &mut self,
186 state: &mut Tree,
187 event: &Event,
188 layout: Layout<'_>,
189 cursor: Cursor,
190 renderer: &Renderer,
191 clipboard: &mut dyn Clipboard,
192 shell: &mut Shell<Message>,
193 viewport: &Rectangle,
194 ) {
195 self.elements
196 .iter_mut()
197 .zip(&mut state.children)
198 .zip(layout.children())
199 .for_each(|((child, state), layout)| {
200 child.as_widget_mut().update(
201 state, event, layout, cursor, renderer, clipboard, shell, viewport,
202 );
203 });
204 }
205
206 fn overlay<'b>(
207 &'b mut self,
208 tree: &'b mut Tree,
209 layout: Layout<'b>,
210 renderer: &Renderer,
211 viewport: &Rectangle,
212 translation: Vector,
213 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
214 self.elements
215 .iter_mut()
216 .zip(&mut tree.children)
217 .zip(layout.children())
218 .find_map(|((child, state), layout)| {
219 child
220 .as_widget_mut()
221 .overlay(state, layout, renderer, viewport, translation)
222 })
223 }
224
225 fn mouse_interaction(
226 &self,
227 state: &Tree,
228 layout: Layout<'_>,
229 cursor: Cursor,
230 viewport: &Rectangle,
231 renderer: &Renderer,
232 ) -> mouse::Interaction {
233 self.elements
234 .iter()
235 .zip(&state.children)
236 .zip(layout.children())
237 .map(|((child, state), layout)| {
238 child
239 .as_widget()
240 .mouse_interaction(state, layout, cursor, viewport, renderer)
241 })
242 .max()
243 .unwrap_or_default()
244 }
245
246 fn draw(
247 &self,
248 state: &Tree,
249 renderer: &mut Renderer,
250 theme: &Theme,
251 style: &renderer::Style,
252 layout: Layout<'_>,
253 cursor: Cursor,
254 viewport: &Rectangle,
255 ) {
256 for ((child, state), layout) in self
257 .elements
258 .iter()
259 .zip(&state.children)
260 .zip(layout.children())
261 {
262 child
263 .as_widget()
264 .draw(state, renderer, theme, style, layout, cursor, viewport);
265 }
266 }
267
268 fn operate(
269 &mut self,
270 state: &mut Tree,
271 layout: Layout<'_>,
272 renderer: &Renderer,
273 operation: &mut dyn Operation<()>,
274 ) {
275 for ((element, state), layout) in self
276 .elements
277 .iter_mut()
278 .zip(&mut state.children)
279 .zip(layout.children())
280 {
281 element
282 .as_widget_mut()
283 .operate(state, layout, renderer, operation);
284 }
285 }
286}
287
288impl<'a, Message, Theme, Renderer> From<Wrap<'a, Message, direction::Vertical, Theme, Renderer>>
289 for Element<'a, Message, Theme, Renderer>
290where
291 Renderer: 'a + renderer::Renderer,
292 Message: 'a,
293 Theme: 'a,
294{
295 fn from(wrap: Wrap<'a, Message, direction::Vertical, Theme, Renderer>) -> Self {
296 Element::new(wrap)
297 }
298}
299
300impl<'a, Message, Theme, Renderer> From<Wrap<'a, Message, direction::Horizontal, Theme, Renderer>>
301 for Element<'a, Message, Theme, Renderer>
302where
303 Renderer: 'a + renderer::Renderer,
304 Message: 'a,
305 Theme: 'a,
306{
307 fn from(wrap: Wrap<'a, Message, direction::Horizontal, Theme, Renderer>) -> Self {
308 Element::new(wrap)
309 }
310}
311
312impl<Message, Renderer, Direction, Theme> Default
313 for Wrap<'_, Message, Direction, Theme, Renderer>
314{
315 fn default() -> Self {
316 Self {
317 elements: vec![],
318 alignment: Alignment::Start,
319 width: Length::Shrink,
320 height: Length::Shrink,
321 max_width: 4_294_967_295.0,
322 max_height: 4_294_967_295.0,
323 padding: Padding::ZERO,
324 spacing: Pixels::ZERO,
325 line_spacing: Pixels::ZERO,
326 line_minimal_length: 10.0,
327 _direction: PhantomData,
328 }
329 }
330}
331pub trait WrapLayout<Renderer>
333where
334 Renderer: renderer::Renderer,
335{
336 fn inner_layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node;
338}
339
340impl<'a, Message, Theme, Renderer> WrapLayout<Renderer>
341 for Wrap<'a, Message, direction::Horizontal, Theme, Renderer>
342where
343 Renderer: renderer::Renderer + 'a,
344{
345 #[allow(clippy::inline_always)]
346 #[inline(always)]
347 fn inner_layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
348 let padding = self.padding;
349 let spacing = self.spacing;
350 let line_spacing = self.line_spacing;
351 #[allow(clippy::cast_precision_loss)] let line_minimal_length = self.line_minimal_length;
353 let limits = limits
354 .shrink(padding)
355 .width(self.width)
356 .height(self.height)
357 .max_width(self.max_width)
358 .max_height(self.max_height);
359 let max_width = limits.max().width;
360
361 let mut children = tree.children.iter_mut();
362 let mut curse = padding.left;
363 let mut deep_curse = padding.left;
364 let mut current_line_height = line_minimal_length;
365 let mut max_main = curse;
366 let mut align = vec![];
367 let mut start = 0;
368 let mut end = 0;
369 let mut nodes: Vec<Node> = self
370 .elements
371 .iter_mut()
372 .map(|elem| {
373 let node_limit = Limits::new(
374 Size::new(limits.min().width, line_minimal_length),
375 limits.max(),
376 );
377 let mut node = elem.as_widget_mut().layout(
378 children.next().expect("wrap missing expected child"),
379 renderer,
380 &node_limit,
381 );
382
383 let size = node.size();
384
385 let offset_init = size.width + spacing.0;
386 let offset = curse + offset_init;
387
388 if offset > max_width {
389 deep_curse += current_line_height + line_spacing.0;
390 align.push((start..end, current_line_height));
391 start = end;
392 end += 1;
393 current_line_height = line_minimal_length;
394 node.move_to_mut(Point::new(padding.left, deep_curse));
395 curse = offset_init + padding.left;
396 } else {
397 node.move_to_mut(Point::new(curse, deep_curse));
398 curse = offset;
399 end += 1;
400 }
401 current_line_height = current_line_height.max(size.height);
402 max_main = max_main.max(curse);
403
404 node
405 })
406 .collect();
407 if end != start {
408 align.push((start..end, current_line_height));
409 }
410 for (range, max_length) in align {
411 nodes[range].iter_mut().for_each(|node| {
412 let size = node.size();
413 let space = Size::new(size.width, max_length);
414 node.align_mut(Alignment::Start, self.alignment, space);
415 });
416 }
417 let (width, height) = (
418 max_main - padding.left,
419 deep_curse - padding.left + current_line_height,
420 );
421 let size = limits.resolve(self.width, self.height, Size::new(width, height));
422
423 Node::with_children(size.expand(padding), nodes)
424 }
425}
426
427impl<'a, Message, Theme, Renderer> WrapLayout<Renderer>
428 for Wrap<'a, Message, direction::Vertical, Theme, Renderer>
429where
430 Renderer: renderer::Renderer + 'a,
431{
432 #[allow(clippy::inline_always)]
433 #[inline(always)]
434 fn inner_layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
435 let padding = self.padding;
436 let spacing = self.spacing;
437 let line_spacing = self.line_spacing;
438 #[allow(clippy::cast_precision_loss)] let line_minimal_length = self.line_minimal_length;
440 let limits = limits
441 .shrink(padding)
442 .width(self.width)
443 .height(self.height)
444 .max_width(self.max_width)
445 .max_height(self.max_height);
446 let max_height = limits.max().height;
447
448 let mut children = tree.children.iter_mut();
449 let mut curse = padding.left;
450 let mut wide_curse = padding.left;
451 let mut current_line_width = line_minimal_length;
452 let mut max_main = curse;
453 let mut align = vec![];
454 let mut start = 0;
455 let mut end = 0;
456 let mut nodes: Vec<Node> = self
457 .elements
458 .iter_mut()
459 .map(|elem| {
460 let node_limit = Limits::new(
461 Size::new(line_minimal_length, limits.min().height),
462 limits.max(),
463 );
464 let mut node = elem.as_widget_mut().layout(
465 children.next().expect("wrap missing expected child"),
466 renderer,
467 &node_limit,
468 );
469
470 let size = node.size();
471
472 let offset_init = size.height + spacing.0;
473 let offset = curse + offset_init;
474
475 if offset > max_height {
476 wide_curse += current_line_width + line_spacing.0;
477 align.push((start..end, current_line_width));
478 start = end;
479 end += 1;
480 current_line_width = line_minimal_length;
481 node = node.move_to(Point::new(wide_curse, padding.left));
482 curse = offset_init + padding.left;
483 } else {
484 node = node.move_to(Point::new(wide_curse, curse));
485 end += 1;
486 curse = offset;
487 }
488 current_line_width = current_line_width.max(size.width);
489 max_main = max_main.max(curse);
490
491 node
492 })
493 .collect();
494 if end != start {
495 align.push((start..end, current_line_width));
496 }
497
498 for (range, max_length) in align {
499 nodes[range].iter_mut().for_each(|node| {
500 let size = node.size();
501 let space = Size::new(max_length, size.height);
502 node.align_mut(self.alignment, Alignment::Start, space);
503 });
504 }
505
506 let (width, height) = (
507 wide_curse - padding.left + current_line_width,
508 max_main - padding.left,
509 );
510 let size = limits.resolve(self.width, self.height, Size::new(width, height));
511
512 Node::with_children(size.expand(padding), nodes)
513 }
514}
515
516pub mod direction {
518 #[derive(Debug)]
520 pub struct Vertical;
521 #[derive(Debug)]
523 pub struct Horizontal;
524}