Skip to main content

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}
525
526#[cfg(test)]
527#[allow(clippy::float_cmp)]
528mod tests {
529    use super::*;
530
531    #[derive(Clone)]
532    enum TestMessage {}
533
534    type TestWrapHorizontal<'a> =
535        Wrap<'a, TestMessage, direction::Horizontal, iced_widget::Theme, iced_widget::Renderer>;
536    type TestWrapVertical<'a> =
537        Wrap<'a, TestMessage, direction::Vertical, iced_widget::Theme, iced_widget::Renderer>;
538
539    // ============================================================================
540    // Constructor Tests - Horizontal
541    // ============================================================================
542
543    #[test]
544    fn wrap_horizontal_new_creates_empty_wrap() {
545        let wrap = TestWrapHorizontal::new();
546        assert_eq!(wrap.elements.len(), 0);
547    }
548
549    #[test]
550    fn wrap_horizontal_new_has_default_values() {
551        let wrap = TestWrapHorizontal::new();
552
553        assert_eq!(wrap.elements.len(), 0);
554        assert_eq!(wrap.alignment, Alignment::Start);
555        assert_eq!(wrap.width, Length::Shrink);
556        assert_eq!(wrap.height, Length::Shrink);
557        assert_eq!(wrap.max_width, 4_294_967_295.0);
558        assert_eq!(wrap.max_height, 4_294_967_295.0);
559        assert_eq!(wrap.padding, Padding::ZERO);
560        assert_eq!(wrap.spacing, Pixels::ZERO);
561        assert_eq!(wrap.line_spacing, Pixels::ZERO);
562        assert_eq!(wrap.line_minimal_length, 10.0);
563    }
564
565    #[test]
566    fn wrap_horizontal_with_elements_creates_wrap_with_elements() {
567        let elements: Vec<Element<TestMessage, iced_widget::Theme, iced_widget::Renderer>> =
568            vec![iced_widget::text::Text::new("Test").into()];
569        let wrap = TestWrapHorizontal::with_elements(elements);
570        assert_eq!(wrap.elements.len(), 1);
571    }
572
573    #[test]
574    fn wrap_horizontal_with_elements_empty_vec() {
575        let elements: Vec<Element<TestMessage, iced_widget::Theme, iced_widget::Renderer>> = vec![];
576        let wrap = TestWrapHorizontal::with_elements(elements);
577        assert_eq!(wrap.elements.len(), 0);
578    }
579
580    // ============================================================================
581    // Constructor Tests - Vertical
582    // ============================================================================
583
584    #[test]
585    fn wrap_vertical_new_creates_empty_wrap() {
586        let wrap = TestWrapVertical::new_vertical();
587        assert_eq!(wrap.elements.len(), 0);
588    }
589
590    #[test]
591    fn wrap_vertical_new_has_default_values() {
592        let wrap = TestWrapVertical::new_vertical();
593
594        assert_eq!(wrap.elements.len(), 0);
595        assert_eq!(wrap.alignment, Alignment::Start);
596        assert_eq!(wrap.width, Length::Shrink);
597        assert_eq!(wrap.height, Length::Shrink);
598        assert_eq!(wrap.max_width, 4_294_967_295.0);
599        assert_eq!(wrap.max_height, 4_294_967_295.0);
600        assert_eq!(wrap.padding, Padding::ZERO);
601        assert_eq!(wrap.spacing, Pixels::ZERO);
602        assert_eq!(wrap.line_spacing, Pixels::ZERO);
603        assert_eq!(wrap.line_minimal_length, 10.0);
604    }
605
606    #[test]
607    fn wrap_vertical_with_elements_creates_wrap_with_elements() {
608        let elements: Vec<Element<TestMessage, iced_widget::Theme, iced_widget::Renderer>> =
609            vec![iced_widget::text::Text::new("Test").into()];
610        let wrap = TestWrapVertical::with_elements_vertical(elements);
611        assert_eq!(wrap.elements.len(), 1);
612    }
613
614    #[test]
615    fn wrap_vertical_with_elements_empty_vec() {
616        let elements: Vec<Element<TestMessage, iced_widget::Theme, iced_widget::Renderer>> = vec![];
617        let wrap = TestWrapVertical::with_elements_vertical(elements);
618        assert_eq!(wrap.elements.len(), 0);
619    }
620
621    // ============================================================================
622    // Push Method Tests
623    // ============================================================================
624
625    #[test]
626    fn wrap_horizontal_push_adds_element() {
627        let wrap = TestWrapHorizontal::new().push(iced_widget::text::Text::new("Test"));
628        assert_eq!(wrap.elements.len(), 1);
629    }
630
631    #[test]
632    fn wrap_horizontal_push_multiple_elements() {
633        let wrap = TestWrapHorizontal::new()
634            .push(iced_widget::text::Text::new("First"))
635            .push(iced_widget::text::Text::new("Second"))
636            .push(iced_widget::text::Text::new("Third"));
637        assert_eq!(wrap.elements.len(), 3);
638    }
639
640    #[test]
641    fn wrap_vertical_push_adds_element() {
642        let wrap = TestWrapVertical::new_vertical().push(iced_widget::text::Text::new("Test"));
643        assert_eq!(wrap.elements.len(), 1);
644    }
645
646    #[test]
647    fn wrap_vertical_push_multiple_elements() {
648        let wrap = TestWrapVertical::new_vertical()
649            .push(iced_widget::text::Text::new("First"))
650            .push(iced_widget::text::Text::new("Second"))
651            .push(iced_widget::text::Text::new("Third"));
652        assert_eq!(wrap.elements.len(), 3);
653    }
654
655    // ============================================================================
656    // Spacing Tests
657    // ============================================================================
658
659    #[test]
660    fn wrap_horizontal_spacing_sets_value() {
661        let wrap = TestWrapHorizontal::new().spacing(10.0);
662        assert_eq!(wrap.spacing, Pixels(10.0));
663    }
664
665    #[test]
666    fn wrap_horizontal_spacing_zero_sets_value() {
667        let wrap = TestWrapHorizontal::new().spacing(0.0);
668        assert_eq!(wrap.spacing, Pixels::ZERO);
669    }
670
671    #[test]
672    fn wrap_vertical_spacing_sets_value() {
673        let wrap = TestWrapVertical::new_vertical().spacing(15.0);
674        assert_eq!(wrap.spacing, Pixels(15.0));
675    }
676
677    // ============================================================================
678    // Line Spacing Tests
679    // ============================================================================
680
681    #[test]
682    fn wrap_horizontal_line_spacing_sets_value() {
683        let wrap = TestWrapHorizontal::new().line_spacing(20.0);
684        assert_eq!(wrap.line_spacing, Pixels(20.0));
685    }
686
687    #[test]
688    fn wrap_horizontal_line_spacing_zero_sets_value() {
689        let wrap = TestWrapHorizontal::new().line_spacing(0.0);
690        assert_eq!(wrap.line_spacing, Pixels::ZERO);
691    }
692
693    #[test]
694    fn wrap_vertical_line_spacing_sets_value() {
695        let wrap = TestWrapVertical::new_vertical().line_spacing(25.0);
696        assert_eq!(wrap.line_spacing, Pixels(25.0));
697    }
698
699    // ============================================================================
700    // Line Minimal Length Tests
701    // ============================================================================
702
703    #[test]
704    fn wrap_horizontal_line_minimal_length_sets_value() {
705        let wrap = TestWrapHorizontal::new().line_minimal_length(50.0);
706        assert_eq!(wrap.line_minimal_length, 50.0);
707    }
708
709    #[test]
710    fn wrap_horizontal_line_minimal_length_default_is_10() {
711        let wrap = TestWrapHorizontal::new();
712        assert_eq!(wrap.line_minimal_length, 10.0);
713    }
714
715    #[test]
716    fn wrap_vertical_line_minimal_length_sets_value() {
717        let wrap = TestWrapVertical::new_vertical().line_minimal_length(100.0);
718        assert_eq!(wrap.line_minimal_length, 100.0);
719    }
720
721    // ============================================================================
722    // Padding Tests
723    // ============================================================================
724
725    #[test]
726    fn wrap_horizontal_padding_sets_value() {
727        let wrap = TestWrapHorizontal::new().padding(10);
728        assert_eq!(wrap.padding, Padding::new(10.0));
729    }
730
731    #[test]
732    fn wrap_horizontal_padding_default_is_zero() {
733        let wrap = TestWrapHorizontal::new();
734        assert_eq!(wrap.padding, Padding::ZERO);
735    }
736
737    #[test]
738    fn wrap_horizontal_padding_with_padding_struct() {
739        let padding = Padding {
740            top: 5.0,
741            right: 10.0,
742            bottom: 15.0,
743            left: 20.0,
744        };
745        let wrap = TestWrapHorizontal::new().padding(padding);
746        assert_eq!(wrap.padding, padding);
747    }
748
749    #[test]
750    fn wrap_vertical_padding_sets_value() {
751        let wrap = TestWrapVertical::new_vertical().padding(12);
752        assert_eq!(wrap.padding, Padding::new(12.0));
753    }
754
755    // ============================================================================
756    // Width Tests
757    // ============================================================================
758
759    #[test]
760    fn wrap_horizontal_width_items_sets_value() {
761        let wrap = TestWrapHorizontal::new().width_items(Length::Fill);
762        assert_eq!(wrap.width, Length::Fill);
763    }
764
765    #[test]
766    fn wrap_horizontal_width_items_default_is_shrink() {
767        let wrap = TestWrapHorizontal::new();
768        assert_eq!(wrap.width, Length::Shrink);
769    }
770
771    #[test]
772    fn wrap_horizontal_width_items_fixed() {
773        let wrap = TestWrapHorizontal::new().width_items(Length::Fixed(200.0));
774        assert_eq!(wrap.width, Length::Fixed(200.0));
775    }
776
777    #[test]
778    fn wrap_vertical_width_items_sets_value() {
779        let wrap = TestWrapVertical::new_vertical().width_items(Length::Fill);
780        assert_eq!(wrap.width, Length::Fill);
781    }
782
783    // ============================================================================
784    // Height Tests
785    // ============================================================================
786
787    #[test]
788    fn wrap_horizontal_height_items_sets_value() {
789        let wrap = TestWrapHorizontal::new().height_items(Length::Fill);
790        assert_eq!(wrap.height, Length::Fill);
791    }
792
793    #[test]
794    fn wrap_horizontal_height_items_default_is_shrink() {
795        let wrap = TestWrapHorizontal::new();
796        assert_eq!(wrap.height, Length::Shrink);
797    }
798
799    #[test]
800    fn wrap_horizontal_height_items_fixed() {
801        let wrap = TestWrapHorizontal::new().height_items(Length::Fixed(150.0));
802        assert_eq!(wrap.height, Length::Fixed(150.0));
803    }
804
805    #[test]
806    fn wrap_vertical_height_items_sets_value() {
807        let wrap = TestWrapVertical::new_vertical().height_items(Length::Fill);
808        assert_eq!(wrap.height, Length::Fill);
809    }
810
811    // ============================================================================
812    // Max Width Tests
813    // ============================================================================
814
815    #[test]
816    fn wrap_horizontal_max_width_sets_value() {
817        let wrap = TestWrapHorizontal::new().max_width(500.0);
818        assert_eq!(wrap.max_width, 500.0);
819    }
820
821    #[test]
822    fn wrap_horizontal_max_width_default() {
823        let wrap = TestWrapHorizontal::new();
824        assert_eq!(wrap.max_width, 4_294_967_295.0);
825    }
826
827    #[test]
828    fn wrap_vertical_max_width_sets_value() {
829        let wrap = TestWrapVertical::new_vertical().max_width(600.0);
830        assert_eq!(wrap.max_width, 600.0);
831    }
832
833    // ============================================================================
834    // Max Height Tests
835    // ============================================================================
836
837    #[test]
838    fn wrap_horizontal_max_height_sets_value() {
839        let wrap = TestWrapHorizontal::new().max_height(400.0);
840        assert_eq!(wrap.max_height, 400.0);
841    }
842
843    #[test]
844    fn wrap_horizontal_max_height_default() {
845        let wrap = TestWrapHorizontal::new();
846        assert_eq!(wrap.max_height, 4_294_967_295.0);
847    }
848
849    #[test]
850    fn wrap_vertical_max_height_sets_value() {
851        let wrap = TestWrapVertical::new_vertical().max_height(300.0);
852        assert_eq!(wrap.max_height, 300.0);
853    }
854
855    // ============================================================================
856    // Alignment Tests
857    // ============================================================================
858
859    #[test]
860    fn wrap_horizontal_align_items_start() {
861        let wrap = TestWrapHorizontal::new().align_items(Alignment::Start);
862        assert_eq!(wrap.alignment, Alignment::Start);
863    }
864
865    #[test]
866    fn wrap_horizontal_align_items_center() {
867        let wrap = TestWrapHorizontal::new().align_items(Alignment::Center);
868        assert_eq!(wrap.alignment, Alignment::Center);
869    }
870
871    #[test]
872    fn wrap_horizontal_align_items_end() {
873        let wrap = TestWrapHorizontal::new().align_items(Alignment::End);
874        assert_eq!(wrap.alignment, Alignment::End);
875    }
876
877    #[test]
878    fn wrap_horizontal_align_items_default_is_start() {
879        let wrap = TestWrapHorizontal::new();
880        assert_eq!(wrap.alignment, Alignment::Start);
881    }
882
883    #[test]
884    fn wrap_vertical_align_items_center() {
885        let wrap = TestWrapVertical::new_vertical().align_items(Alignment::Center);
886        assert_eq!(wrap.alignment, Alignment::Center);
887    }
888
889    // ============================================================================
890    // Method Chaining Tests
891    // ============================================================================
892
893    #[test]
894    fn wrap_horizontal_chaining_all_methods() {
895        let wrap = TestWrapHorizontal::new()
896            .push(iced_widget::text::Text::new("Test"))
897            .spacing(5.0)
898            .line_spacing(10.0)
899            .line_minimal_length(20.0)
900            .padding(15)
901            .width_items(Length::Fill)
902            .height_items(Length::Shrink)
903            .max_width(800.0)
904            .max_height(600.0)
905            .align_items(Alignment::Center);
906
907        assert_eq!(wrap.elements.len(), 1);
908        assert_eq!(wrap.spacing, Pixels(5.0));
909        assert_eq!(wrap.line_spacing, Pixels(10.0));
910        assert_eq!(wrap.line_minimal_length, 20.0);
911        assert_eq!(wrap.padding, Padding::new(15.0));
912        assert_eq!(wrap.width, Length::Fill);
913        assert_eq!(wrap.height, Length::Shrink);
914        assert_eq!(wrap.max_width, 800.0);
915        assert_eq!(wrap.max_height, 600.0);
916        assert_eq!(wrap.alignment, Alignment::Center);
917    }
918
919    #[test]
920    fn wrap_vertical_chaining_all_methods() {
921        let wrap = TestWrapVertical::new_vertical()
922            .push(iced_widget::text::Text::new("Test"))
923            .spacing(8.0)
924            .line_spacing(12.0)
925            .line_minimal_length(25.0)
926            .padding(20)
927            .width_items(Length::Shrink)
928            .height_items(Length::Fill)
929            .max_width(700.0)
930            .max_height(500.0)
931            .align_items(Alignment::End);
932
933        assert_eq!(wrap.elements.len(), 1);
934        assert_eq!(wrap.spacing, Pixels(8.0));
935        assert_eq!(wrap.line_spacing, Pixels(12.0));
936        assert_eq!(wrap.line_minimal_length, 25.0);
937        assert_eq!(wrap.padding, Padding::new(20.0));
938        assert_eq!(wrap.width, Length::Shrink);
939        assert_eq!(wrap.height, Length::Fill);
940        assert_eq!(wrap.max_width, 700.0);
941        assert_eq!(wrap.max_height, 500.0);
942        assert_eq!(wrap.alignment, Alignment::End);
943    }
944
945    // ============================================================================
946    // Widget Size Tests
947    // ============================================================================
948
949    #[test]
950    fn wrap_horizontal_size_method_returns_correct_size() {
951        let wrap = TestWrapHorizontal::new()
952            .width_items(Length::Fixed(100.0))
953            .height_items(Length::Fixed(50.0));
954
955        let size = Widget::<TestMessage, iced_widget::Theme, iced_widget::Renderer>::size(&wrap);
956        assert_eq!(size.width, Length::Fixed(100.0));
957        assert_eq!(size.height, Length::Fixed(50.0));
958    }
959
960    #[test]
961    fn wrap_vertical_size_method_returns_correct_size() {
962        let wrap = TestWrapVertical::new_vertical()
963            .width_items(Length::Fill)
964            .height_items(Length::Shrink);
965
966        let size = Widget::<TestMessage, iced_widget::Theme, iced_widget::Renderer>::size(&wrap);
967        assert_eq!(size.width, Length::Fill);
968        assert_eq!(size.height, Length::Shrink);
969    }
970
971    // ============================================================================
972    // Default Trait Tests
973    // ============================================================================
974
975    #[test]
976    fn wrap_horizontal_default_creates_empty_wrap_with_defaults() {
977        let wrap: TestWrapHorizontal = Wrap::default();
978
979        assert_eq!(wrap.elements.len(), 0);
980        assert_eq!(wrap.alignment, Alignment::Start);
981        assert_eq!(wrap.width, Length::Shrink);
982        assert_eq!(wrap.height, Length::Shrink);
983        assert_eq!(wrap.max_width, 4_294_967_295.0);
984        assert_eq!(wrap.max_height, 4_294_967_295.0);
985        assert_eq!(wrap.padding, Padding::ZERO);
986        assert_eq!(wrap.spacing, Pixels::ZERO);
987        assert_eq!(wrap.line_spacing, Pixels::ZERO);
988        assert_eq!(wrap.line_minimal_length, 10.0);
989    }
990
991    #[test]
992    fn wrap_vertical_default_creates_empty_wrap_with_defaults() {
993        let wrap: TestWrapVertical = Wrap::default();
994
995        assert_eq!(wrap.elements.len(), 0);
996        assert_eq!(wrap.alignment, Alignment::Start);
997        assert_eq!(wrap.width, Length::Shrink);
998        assert_eq!(wrap.height, Length::Shrink);
999        assert_eq!(wrap.max_width, 4_294_967_295.0);
1000        assert_eq!(wrap.max_height, 4_294_967_295.0);
1001        assert_eq!(wrap.padding, Padding::ZERO);
1002        assert_eq!(wrap.spacing, Pixels::ZERO);
1003        assert_eq!(wrap.line_spacing, Pixels::ZERO);
1004        assert_eq!(wrap.line_minimal_length, 10.0);
1005    }
1006}