iced_widget/
column.rs

1//! Distribute content vertically.
2use crate::core::alignment::{self, Alignment};
3use crate::core::layout;
4use crate::core::mouse;
5use crate::core::overlay;
6use crate::core::renderer;
7use crate::core::widget::{Operation, Tree};
8use crate::core::{
9    Clipboard, Element, Event, Layout, Length, Padding, Pixels, Rectangle,
10    Shell, Size, Vector, Widget,
11};
12
13/// A container that distributes its contents vertically.
14///
15/// # Example
16/// ```no_run
17/// # mod iced { pub mod widget { pub use iced_widget::*; } }
18/// # pub type State = ();
19/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
20/// use iced::widget::{button, column};
21///
22/// #[derive(Debug, Clone)]
23/// enum Message {
24///     // ...
25/// }
26///
27/// fn view(state: &State) -> Element<'_, Message> {
28///     column![
29///         "I am on top!",
30///         button("I am in the center!"),
31///         "I am below.",
32///     ].into()
33/// }
34/// ```
35pub struct Column<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
36{
37    spacing: f32,
38    padding: Padding,
39    width: Length,
40    height: Length,
41    max_width: f32,
42    align: Alignment,
43    clip: bool,
44    children: Vec<Element<'a, Message, Theme, Renderer>>,
45}
46
47impl<'a, Message, Theme, Renderer> Column<'a, Message, Theme, Renderer>
48where
49    Renderer: crate::core::Renderer,
50{
51    /// Creates an empty [`Column`].
52    pub fn new() -> Self {
53        Self::from_vec(Vec::new())
54    }
55
56    /// Creates a [`Column`] with the given capacity.
57    pub fn with_capacity(capacity: usize) -> Self {
58        Self::from_vec(Vec::with_capacity(capacity))
59    }
60
61    /// Creates a [`Column`] with the given elements.
62    pub fn with_children(
63        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
64    ) -> Self {
65        let iterator = children.into_iter();
66
67        Self::with_capacity(iterator.size_hint().0).extend(iterator)
68    }
69
70    /// Creates a [`Column`] from an already allocated [`Vec`].
71    ///
72    /// Keep in mind that the [`Column`] will not inspect the [`Vec`], which means
73    /// it won't automatically adapt to the sizing strategy of its contents.
74    ///
75    /// If any of the children have a [`Length::Fill`] strategy, you will need to
76    /// call [`Column::width`] or [`Column::height`] accordingly.
77    pub fn from_vec(
78        children: Vec<Element<'a, Message, Theme, Renderer>>,
79    ) -> Self {
80        Self {
81            spacing: 0.0,
82            padding: Padding::ZERO,
83            width: Length::Shrink,
84            height: Length::Shrink,
85            max_width: f32::INFINITY,
86            align: Alignment::Start,
87            clip: false,
88            children,
89        }
90    }
91
92    /// Sets the vertical spacing _between_ elements.
93    ///
94    /// Custom margins per element do not exist in iced. You should use this
95    /// method instead! While less flexible, it helps you keep spacing between
96    /// elements consistent.
97    pub fn spacing(mut self, amount: impl Into<Pixels>) -> Self {
98        self.spacing = amount.into().0;
99        self
100    }
101
102    /// Sets the [`Padding`] of the [`Column`].
103    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
104        self.padding = padding.into();
105        self
106    }
107
108    /// Sets the width of the [`Column`].
109    pub fn width(mut self, width: impl Into<Length>) -> Self {
110        self.width = width.into();
111        self
112    }
113
114    /// Sets the height of the [`Column`].
115    pub fn height(mut self, height: impl Into<Length>) -> Self {
116        self.height = height.into();
117        self
118    }
119
120    /// Sets the maximum width of the [`Column`].
121    pub fn max_width(mut self, max_width: impl Into<Pixels>) -> Self {
122        self.max_width = max_width.into().0;
123        self
124    }
125
126    /// Sets the horizontal alignment of the contents of the [`Column`] .
127    pub fn align_x(mut self, align: impl Into<alignment::Horizontal>) -> Self {
128        self.align = Alignment::from(align.into());
129        self
130    }
131
132    /// Sets whether the contents of the [`Column`] should be clipped on
133    /// overflow.
134    pub fn clip(mut self, clip: bool) -> Self {
135        self.clip = clip;
136        self
137    }
138
139    /// Adds an element to the [`Column`].
140    pub fn push(
141        mut self,
142        child: impl Into<Element<'a, Message, Theme, Renderer>>,
143    ) -> Self {
144        let child = child.into();
145        let child_size = child.as_widget().size_hint();
146
147        if !child_size.is_void() {
148            self.width = self.width.enclose(child_size.width);
149            self.height = self.height.enclose(child_size.height);
150            self.children.push(child);
151        }
152
153        self
154    }
155
156    /// Extends the [`Column`] with the given children.
157    pub fn extend(
158        self,
159        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
160    ) -> Self {
161        children.into_iter().fold(self, Self::push)
162    }
163
164    /// Turns the [`Column`] into a [`Wrapping`] column.
165    ///
166    /// The original alignment of the [`Column`] is preserved per column wrapped.
167    pub fn wrap(self) -> Wrapping<'a, Message, Theme, Renderer> {
168        Wrapping {
169            column: self,
170            horizontal_spacing: None,
171            align_y: alignment::Vertical::Top,
172        }
173    }
174}
175
176impl<Message, Renderer> Default for Column<'_, Message, Renderer>
177where
178    Renderer: crate::core::Renderer,
179{
180    fn default() -> Self {
181        Self::new()
182    }
183}
184
185impl<'a, Message, Theme, Renderer: crate::core::Renderer>
186    FromIterator<Element<'a, Message, Theme, Renderer>>
187    for Column<'a, Message, Theme, Renderer>
188{
189    fn from_iter<
190        T: IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
191    >(
192        iter: T,
193    ) -> Self {
194        Self::with_children(iter)
195    }
196}
197
198impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
199    for Column<'_, Message, Theme, Renderer>
200where
201    Renderer: crate::core::Renderer,
202{
203    fn children(&self) -> Vec<Tree> {
204        self.children.iter().map(Tree::new).collect()
205    }
206
207    fn diff(&self, tree: &mut Tree) {
208        tree.diff_children(&self.children);
209    }
210
211    fn size(&self) -> Size<Length> {
212        Size {
213            width: self.width,
214            height: self.height,
215        }
216    }
217
218    fn layout(
219        &mut self,
220        tree: &mut Tree,
221        renderer: &Renderer,
222        limits: &layout::Limits,
223    ) -> layout::Node {
224        let limits = limits.max_width(self.max_width);
225
226        layout::flex::resolve(
227            layout::flex::Axis::Vertical,
228            renderer,
229            &limits,
230            self.width,
231            self.height,
232            self.padding,
233            self.spacing,
234            self.align,
235            &mut self.children,
236            &mut tree.children,
237        )
238    }
239
240    fn operate(
241        &mut self,
242        tree: &mut Tree,
243        layout: Layout<'_>,
244        renderer: &Renderer,
245        operation: &mut dyn Operation,
246    ) {
247        operation.container(None, layout.bounds());
248        operation.traverse(&mut |operation| {
249            self.children
250                .iter_mut()
251                .zip(&mut tree.children)
252                .zip(layout.children())
253                .for_each(|((child, state), layout)| {
254                    child
255                        .as_widget_mut()
256                        .operate(state, layout, renderer, operation);
257                });
258        });
259    }
260
261    fn update(
262        &mut self,
263        tree: &mut Tree,
264        event: &Event,
265        layout: Layout<'_>,
266        cursor: mouse::Cursor,
267        renderer: &Renderer,
268        clipboard: &mut dyn Clipboard,
269        shell: &mut Shell<'_, Message>,
270        viewport: &Rectangle,
271    ) {
272        for ((child, tree), layout) in self
273            .children
274            .iter_mut()
275            .zip(&mut tree.children)
276            .zip(layout.children())
277        {
278            child.as_widget_mut().update(
279                tree, event, layout, cursor, renderer, clipboard, shell,
280                viewport,
281            );
282        }
283    }
284
285    fn mouse_interaction(
286        &self,
287        tree: &Tree,
288        layout: Layout<'_>,
289        cursor: mouse::Cursor,
290        viewport: &Rectangle,
291        renderer: &Renderer,
292    ) -> mouse::Interaction {
293        self.children
294            .iter()
295            .zip(&tree.children)
296            .zip(layout.children())
297            .map(|((child, tree), layout)| {
298                child
299                    .as_widget()
300                    .mouse_interaction(tree, layout, cursor, viewport, renderer)
301            })
302            .max()
303            .unwrap_or_default()
304    }
305
306    fn draw(
307        &self,
308        tree: &Tree,
309        renderer: &mut Renderer,
310        theme: &Theme,
311        style: &renderer::Style,
312        layout: Layout<'_>,
313        cursor: mouse::Cursor,
314        viewport: &Rectangle,
315    ) {
316        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
317            let viewport = if self.clip {
318                &clipped_viewport
319            } else {
320                viewport
321            };
322
323            for ((child, tree), layout) in self
324                .children
325                .iter()
326                .zip(&tree.children)
327                .zip(layout.children())
328                .filter(|(_, layout)| layout.bounds().intersects(viewport))
329            {
330                child.as_widget().draw(
331                    tree, renderer, theme, style, layout, cursor, viewport,
332                );
333            }
334        }
335    }
336
337    fn overlay<'b>(
338        &'b mut self,
339        tree: &'b mut Tree,
340        layout: Layout<'b>,
341        renderer: &Renderer,
342        viewport: &Rectangle,
343        translation: Vector,
344    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
345        overlay::from_children(
346            &mut self.children,
347            tree,
348            layout,
349            renderer,
350            viewport,
351            translation,
352        )
353    }
354}
355
356impl<'a, Message, Theme, Renderer> From<Column<'a, Message, Theme, Renderer>>
357    for Element<'a, Message, Theme, Renderer>
358where
359    Message: 'a,
360    Theme: 'a,
361    Renderer: crate::core::Renderer + 'a,
362{
363    fn from(column: Column<'a, Message, Theme, Renderer>) -> Self {
364        Self::new(column)
365    }
366}
367
368/// A [`Column`] that wraps its contents.
369///
370/// Create a [`Column`] first, and then call [`Column::wrap`] to
371/// obtain a [`Column`] that wraps its contents.
372///
373/// The original alignment of the [`Column`] is preserved per column wrapped.
374#[allow(missing_debug_implementations)]
375pub struct Wrapping<
376    'a,
377    Message,
378    Theme = crate::Theme,
379    Renderer = crate::Renderer,
380> {
381    column: Column<'a, Message, Theme, Renderer>,
382    horizontal_spacing: Option<f32>,
383    align_y: alignment::Vertical,
384}
385
386impl<Message, Theme, Renderer> Wrapping<'_, Message, Theme, Renderer> {
387    /// Sets the horizontal spacing _between_ columns.
388    pub fn horizontal_spacing(mut self, amount: impl Into<Pixels>) -> Self {
389        self.horizontal_spacing = Some(amount.into().0);
390        self
391    }
392
393    /// Sets the vertical alignment of the wrapping [`Column`].
394    pub fn align_x(mut self, align_y: impl Into<alignment::Vertical>) -> Self {
395        self.align_y = align_y.into();
396        self
397    }
398}
399
400impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
401    for Wrapping<'_, Message, Theme, Renderer>
402where
403    Renderer: crate::core::Renderer,
404{
405    fn children(&self) -> Vec<Tree> {
406        self.column.children()
407    }
408
409    fn diff(&self, tree: &mut Tree) {
410        self.column.diff(tree);
411    }
412
413    fn size(&self) -> Size<Length> {
414        self.column.size()
415    }
416
417    fn layout(
418        &mut self,
419        tree: &mut Tree,
420        renderer: &Renderer,
421        limits: &layout::Limits,
422    ) -> layout::Node {
423        let limits = limits
424            .width(self.column.width)
425            .height(self.column.height)
426            .shrink(self.column.padding);
427
428        let child_limits = limits.loose();
429        let spacing = self.column.spacing;
430        let horizontal_spacing = self.horizontal_spacing.unwrap_or(spacing);
431        let max_height = limits.max().height;
432
433        let mut children: Vec<layout::Node> = Vec::new();
434        let mut intrinsic_size = Size::ZERO;
435        let mut column_start = 0;
436        let mut column_width = 0.0;
437        let mut x = 0.0;
438        let mut y = 0.0;
439
440        let align_factor = match self.column.align {
441            Alignment::Start => 0.0,
442            Alignment::Center => 2.0,
443            Alignment::End => 1.0,
444        };
445
446        let align_x = |column_start: std::ops::Range<usize>,
447                       column_width: f32,
448                       children: &mut Vec<layout::Node>| {
449            if align_factor != 0.0 {
450                for node in &mut children[column_start] {
451                    let width = node.size().width;
452
453                    node.translate_mut(Vector::new(
454                        (column_width - width) / align_factor,
455                        0.0,
456                    ));
457                }
458            }
459        };
460
461        for (i, child) in self.column.children.iter_mut().enumerate() {
462            let node = child.as_widget_mut().layout(
463                &mut tree.children[i],
464                renderer,
465                &child_limits,
466            );
467
468            let child_size = node.size();
469
470            if y != 0.0 && y + child_size.height > max_height {
471                intrinsic_size.height = intrinsic_size.height.max(y - spacing);
472
473                align_x(column_start..i, column_width, &mut children);
474
475                x += column_width + horizontal_spacing;
476                y = 0.0;
477                column_start = i;
478                column_width = 0.0;
479            }
480
481            column_width = column_width.max(child_size.width);
482
483            children.push(node.move_to((
484                x + self.column.padding.left,
485                y + self.column.padding.top,
486            )));
487
488            y += child_size.height + spacing;
489        }
490
491        if y != 0.0 {
492            intrinsic_size.height = intrinsic_size.height.max(y - spacing);
493        }
494
495        intrinsic_size.width = x + column_width;
496        align_x(column_start..children.len(), column_width, &mut children);
497
498        let align_factor = match self.align_y {
499            alignment::Vertical::Top => 0.0,
500            alignment::Vertical::Center => 2.0,
501            alignment::Vertical::Bottom => 1.0,
502        };
503
504        if align_factor != 0.0 {
505            let total_height = intrinsic_size.height;
506
507            let mut column_start = 0;
508
509            for i in 0..children.len() {
510                let bounds = children[i].bounds();
511                let column_height = bounds.y + bounds.height;
512
513                let next_y = children
514                    .get(i + 1)
515                    .map(|node| node.bounds().y)
516                    .unwrap_or_default();
517
518                if next_y == 0.0 {
519                    let translation = Vector::new(
520                        0.0,
521                        (total_height - column_height) / align_factor,
522                    );
523
524                    for node in &mut children[column_start..=i] {
525                        node.translate_mut(translation);
526                    }
527
528                    column_start = i + 1;
529                }
530            }
531        }
532
533        let size = limits.resolve(
534            self.column.width,
535            self.column.height,
536            intrinsic_size,
537        );
538
539        layout::Node::with_children(size.expand(self.column.padding), children)
540    }
541
542    fn operate(
543        &mut self,
544        tree: &mut Tree,
545        layout: Layout<'_>,
546        renderer: &Renderer,
547        operation: &mut dyn Operation,
548    ) {
549        self.column.operate(tree, layout, renderer, operation);
550    }
551
552    fn update(
553        &mut self,
554        tree: &mut Tree,
555        event: &Event,
556        layout: Layout<'_>,
557        cursor: mouse::Cursor,
558        renderer: &Renderer,
559        clipboard: &mut dyn Clipboard,
560        shell: &mut Shell<'_, Message>,
561        viewport: &Rectangle,
562    ) {
563        self.column.update(
564            tree, event, layout, cursor, renderer, clipboard, shell, viewport,
565        );
566    }
567
568    fn mouse_interaction(
569        &self,
570        tree: &Tree,
571        layout: Layout<'_>,
572        cursor: mouse::Cursor,
573        viewport: &Rectangle,
574        renderer: &Renderer,
575    ) -> mouse::Interaction {
576        self.column
577            .mouse_interaction(tree, layout, cursor, viewport, renderer)
578    }
579
580    fn draw(
581        &self,
582        tree: &Tree,
583        renderer: &mut Renderer,
584        theme: &Theme,
585        style: &renderer::Style,
586        layout: Layout<'_>,
587        cursor: mouse::Cursor,
588        viewport: &Rectangle,
589    ) {
590        self.column
591            .draw(tree, renderer, theme, style, layout, cursor, viewport);
592    }
593
594    fn overlay<'b>(
595        &'b mut self,
596        tree: &'b mut Tree,
597        layout: Layout<'b>,
598        renderer: &Renderer,
599        viewport: &Rectangle,
600        translation: Vector,
601    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
602        self.column
603            .overlay(tree, layout, renderer, viewport, translation)
604    }
605}
606
607impl<'a, Message, Theme, Renderer> From<Wrapping<'a, Message, Theme, Renderer>>
608    for Element<'a, Message, Theme, Renderer>
609where
610    Message: 'a,
611    Theme: 'a,
612    Renderer: crate::core::Renderer + 'a,
613{
614    fn from(column: Wrapping<'a, Message, Theme, Renderer>) -> Self {
615        Self::new(column)
616    }
617}