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        renderer: &Renderer,
219        operation: &mut dyn Operation,
220    ) {
221        operation.container(None, layout.bounds());
222        operation.traverse(&mut |operation| {
223            self.children
224                .iter_mut()
225                .zip(&mut tree.children)
226                .zip(layout.children())
227                .for_each(|((child, state), layout)| {
228                    child
229                        .as_widget_mut()
230                        .operate(state, layout, renderer, operation);
231                });
232        });
233    }
234
235    fn update(
236        &mut self,
237        tree: &mut Tree,
238        event: &Event,
239        layout: Layout<'_>,
240        cursor: mouse::Cursor,
241        renderer: &Renderer,
242        shell: &mut Shell<'_, Message>,
243        viewport: &Rectangle,
244    ) {
245        for ((child, tree), layout) in self
246            .children
247            .iter_mut()
248            .zip(&mut tree.children)
249            .zip(layout.children())
250        {
251            child
252                .as_widget_mut()
253                .update(tree, event, layout, cursor, renderer, shell, viewport);
254        }
255    }
256
257    fn mouse_interaction(
258        &self,
259        tree: &Tree,
260        layout: Layout<'_>,
261        cursor: mouse::Cursor,
262        viewport: &Rectangle,
263        renderer: &Renderer,
264    ) -> mouse::Interaction {
265        self.children
266            .iter()
267            .zip(&tree.children)
268            .zip(layout.children())
269            .map(|((child, tree), layout)| {
270                child
271                    .as_widget()
272                    .mouse_interaction(tree, layout, cursor, viewport, renderer)
273            })
274            .max()
275            .unwrap_or_default()
276    }
277
278    fn draw(
279        &self,
280        tree: &Tree,
281        renderer: &mut Renderer,
282        theme: &Theme,
283        style: &renderer::Style,
284        layout: Layout<'_>,
285        cursor: mouse::Cursor,
286        viewport: &Rectangle,
287    ) {
288        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
289            let viewport = if self.clip {
290                &clipped_viewport
291            } else {
292                viewport
293            };
294
295            for ((child, tree), layout) in self
296                .children
297                .iter()
298                .zip(&tree.children)
299                .zip(layout.children())
300                .filter(|(_, layout)| layout.bounds().intersects(viewport))
301            {
302                child
303                    .as_widget()
304                    .draw(tree, renderer, theme, style, layout, cursor, viewport);
305            }
306        }
307    }
308
309    fn overlay<'b>(
310        &'b mut self,
311        tree: &'b mut Tree,
312        layout: Layout<'b>,
313        renderer: &Renderer,
314        viewport: &Rectangle,
315        translation: Vector,
316    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
317        overlay::from_children(
318            &mut self.children,
319            tree,
320            layout,
321            renderer,
322            viewport,
323            translation,
324        )
325    }
326}
327
328impl<'a, Message, Theme, Renderer> From<Column<'a, Message, Theme, Renderer>>
329    for Element<'a, Message, Theme, Renderer>
330where
331    Message: 'a,
332    Theme: 'a,
333    Renderer: crate::core::Renderer + 'a,
334{
335    fn from(column: Column<'a, Message, Theme, Renderer>) -> Self {
336        Self::new(column)
337    }
338}
339
340/// A [`Column`] that wraps its contents.
341///
342/// Create a [`Column`] first, and then call [`Column::wrap`] to
343/// obtain a [`Column`] that wraps its contents.
344///
345/// The original alignment of the [`Column`] is preserved per column wrapped.
346#[allow(missing_debug_implementations)]
347pub struct Wrapping<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
348    column: Column<'a, Message, Theme, Renderer>,
349    horizontal_spacing: Option<f32>,
350    align_y: alignment::Vertical,
351}
352
353impl<Message, Theme, Renderer> Wrapping<'_, Message, Theme, Renderer> {
354    /// Sets the horizontal spacing _between_ columns.
355    pub fn horizontal_spacing(mut self, amount: impl Into<Pixels>) -> Self {
356        self.horizontal_spacing = Some(amount.into().0);
357        self
358    }
359
360    /// Sets the vertical alignment of the wrapping [`Column`].
361    pub fn align_y(mut self, align_y: impl Into<alignment::Vertical>) -> Self {
362        self.align_y = align_y.into();
363        self
364    }
365}
366
367impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
368    for Wrapping<'_, Message, Theme, Renderer>
369where
370    Renderer: crate::core::Renderer,
371{
372    fn diff(&mut self, tree: &mut Tree) {
373        self.column.diff(tree);
374    }
375
376    fn size(&self) -> Size<Length> {
377        self.column.size()
378    }
379
380    fn layout(
381        &mut self,
382        tree: &mut Tree,
383        renderer: &Renderer,
384        limits: &layout::Limits,
385    ) -> layout::Node {
386        let limits = limits
387            .width(self.column.width)
388            .height(self.column.height)
389            .shrink(self.column.padding);
390
391        let child_limits = limits.loose();
392        let spacing = self.column.spacing;
393        let horizontal_spacing = self.horizontal_spacing.unwrap_or(spacing);
394        let max_height = limits.max().height;
395
396        let mut children: Vec<layout::Node> = Vec::new();
397        let mut intrinsic_size = Size::ZERO;
398        let mut column_start = 0;
399        let mut column_width = 0.0;
400        let mut x = 0.0;
401        let mut y = 0.0;
402
403        let align_factor = match self.column.align {
404            Alignment::Start => 0.0,
405            Alignment::Center => 2.0,
406            Alignment::End => 1.0,
407        };
408
409        let align_x = |column_start: std::ops::Range<usize>,
410                       column_width: f32,
411                       children: &mut Vec<layout::Node>| {
412            if align_factor != 0.0 {
413                for node in &mut children[column_start] {
414                    let width = node.size().width;
415
416                    node.translate_mut(Vector::new((column_width - width) / align_factor, 0.0));
417                }
418            }
419        };
420
421        for (i, child) in self.column.children.iter_mut().enumerate() {
422            let node = child
423                .as_widget_mut()
424                .layout(&mut tree.children[i], renderer, &child_limits);
425
426            let child_size = node.size();
427
428            if y != 0.0 && y + child_size.height > max_height {
429                intrinsic_size.height = intrinsic_size.height.max(y - spacing);
430
431                align_x(column_start..i, column_width, &mut children);
432
433                x += column_width + horizontal_spacing;
434                y = 0.0;
435                column_start = i;
436                column_width = 0.0;
437            }
438
439            column_width = column_width.max(child_size.width);
440
441            children
442                .push(node.move_to((x + self.column.padding.left, y + self.column.padding.top)));
443
444            y += child_size.height + spacing;
445        }
446
447        if y != 0.0 {
448            intrinsic_size.height = intrinsic_size.height.max(y - spacing);
449        }
450
451        intrinsic_size.width = x + column_width;
452        align_x(column_start..children.len(), column_width, &mut children);
453
454        let align_factor = match self.align_y {
455            alignment::Vertical::Top => 0.0,
456            alignment::Vertical::Center => 2.0,
457            alignment::Vertical::Bottom => 1.0,
458        };
459
460        if align_factor != 0.0 {
461            let total_height = intrinsic_size.height;
462
463            let mut column_start = 0;
464
465            for i in 0..children.len() {
466                let bounds = children[i].bounds();
467                let column_height = bounds.y + bounds.height;
468
469                let next_y = children
470                    .get(i + 1)
471                    .map(|node| node.bounds().y)
472                    .unwrap_or_default();
473
474                if next_y == 0.0 {
475                    let translation =
476                        Vector::new(0.0, (total_height - column_height) / align_factor);
477
478                    for node in &mut children[column_start..=i] {
479                        node.translate_mut(translation);
480                    }
481
482                    column_start = i + 1;
483                }
484            }
485        }
486
487        let size = limits.resolve(self.column.width, self.column.height, intrinsic_size);
488
489        layout::Node::with_children(size.expand(self.column.padding), children)
490    }
491
492    fn operate(
493        &mut self,
494        tree: &mut Tree,
495        layout: Layout<'_>,
496        renderer: &Renderer,
497        operation: &mut dyn Operation,
498    ) {
499        self.column.operate(tree, layout, renderer, operation);
500    }
501
502    fn update(
503        &mut self,
504        tree: &mut Tree,
505        event: &Event,
506        layout: Layout<'_>,
507        cursor: mouse::Cursor,
508        renderer: &Renderer,
509        shell: &mut Shell<'_, Message>,
510        viewport: &Rectangle,
511    ) {
512        self.column
513            .update(tree, event, layout, cursor, renderer, shell, viewport);
514    }
515
516    fn mouse_interaction(
517        &self,
518        tree: &Tree,
519        layout: Layout<'_>,
520        cursor: mouse::Cursor,
521        viewport: &Rectangle,
522        renderer: &Renderer,
523    ) -> mouse::Interaction {
524        self.column
525            .mouse_interaction(tree, layout, cursor, viewport, renderer)
526    }
527
528    fn draw(
529        &self,
530        tree: &Tree,
531        renderer: &mut Renderer,
532        theme: &Theme,
533        style: &renderer::Style,
534        layout: Layout<'_>,
535        cursor: mouse::Cursor,
536        viewport: &Rectangle,
537    ) {
538        self.column
539            .draw(tree, renderer, theme, style, layout, cursor, viewport);
540    }
541
542    fn overlay<'b>(
543        &'b mut self,
544        tree: &'b mut Tree,
545        layout: Layout<'b>,
546        renderer: &Renderer,
547        viewport: &Rectangle,
548        translation: Vector,
549    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
550        self.column
551            .overlay(tree, layout, renderer, viewport, translation)
552    }
553}
554
555impl<'a, Message, Theme, Renderer> From<Wrapping<'a, Message, Theme, Renderer>>
556    for Element<'a, Message, Theme, Renderer>
557where
558    Message: 'a,
559    Theme: 'a,
560    Renderer: crate::core::Renderer + 'a,
561{
562    fn from(column: Wrapping<'a, Message, Theme, Renderer>) -> Self {
563        Self::new(column)
564    }
565}