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