iced_aw/widget/
wrap.rs

1//! A widget that displays its children in multiple horizontal or vertical runs.
2//!
3//! *This API requires the following crate features to be activated: `wrap`*
4use 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/// A container that distributes its contents horizontally.
15#[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    /// The elements to distribute.
24    pub elements: Vec<Element<'a, Message, Theme, Renderer>>,
25    /// The alignment of the [`Wrap`].
26    pub alignment: Alignment,
27    /// The width of the [`Wrap`].
28    pub width: Length,
29    /// The height of the [`Wrap`].
30    pub height: Length,
31    /// The maximum width of the [`Wrap`].
32    pub max_width: f32,
33    /// The maximum height of the [`Wrap`].
34    pub max_height: f32,
35    /// The padding of each element of the [`Wrap`].
36    pub padding: Padding,
37    /// The spacing between each element of the [`Wrap`].
38    pub spacing: Pixels,
39    /// The spacing between each line of the [`Wrap`].
40    pub line_spacing: Pixels,
41    /// The minimal length of each line of the [`Wrap`].
42    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    /// Creates an empty horizontal [`Wrap`].
49    #[must_use]
50    pub fn new() -> Self {
51        Self::with_elements(Vec::new())
52    }
53
54    /// Creates a [`Wrap`] with the given elements.
55    ///
56    /// It expects:
57    ///     * the vector containing the [`Element`]s for this [`Wrap`].
58    #[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    /// Creates an empty vertical [`Wrap`].
69    #[must_use]
70    pub fn new_vertical() -> Self {
71        Self::with_elements_vertical(Vec::new())
72    }
73
74    /// Creates a [`Wrap`] with the given elements.
75    ///
76    /// It expects:
77    ///     * the vector containing the [`Element`]s for this [`Wrap`].
78    #[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    /// Sets the spacing of the [`Wrap`].
89    #[must_use]
90    pub fn spacing(mut self, spacing: impl Into<Pixels>) -> Self {
91        self.spacing = spacing.into();
92        self
93    }
94
95    /// Sets the spacing of the lines of the [`Wrap`].
96    #[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    /// Sets the minimal length of the lines of the [`Wrap`].
103    #[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    /// Sets the padding of the elements in the [`Wrap`].
110    #[must_use]
111    pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
112        self.padding = padding.into();
113        self
114    }
115
116    /// Sets the width of the [`Wrap`].
117    #[must_use]
118    pub const fn width_items(mut self, width: Length) -> Self {
119        self.width = width;
120        self
121    }
122
123    /// Sets the height of the [`Wrap`].
124    #[must_use]
125    pub const fn height_items(mut self, height: Length) -> Self {
126        self.height = height;
127        self
128    }
129
130    /// Sets the maximum width of the [`Wrap`].
131    #[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    /// Sets the maximum height of the [`Wrap`].
138    #[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    /// Sets the alignment of the [`Wrap`].
145    #[must_use]
146    pub const fn align_items(mut self, align: Alignment) -> Self {
147        self.alignment = align;
148        self
149    }
150
151    /// Pushes an [`Element`] to the [`Wrap`].
152    #[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}
331/// A inner layout of the [`Wrap`].
332pub trait WrapLayout<Renderer>
333where
334    Renderer: renderer::Renderer,
335{
336    /// A inner layout of the [`Wrap`].
337    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)] // TODO: possible precision loss
352        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)] // TODO: possible precision loss
439        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
516/// An optional directional attribute of the [`Wrap`].
517pub mod direction {
518    /// An vertical direction of the [`Wrap`](super::Wrap).
519    #[derive(Debug)]
520    pub struct Vertical;
521    /// An horizontal direction of the [`Wrap`](super::Wrap).
522    #[derive(Debug)]
523    pub struct Horizontal;
524}