Skip to main content

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