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