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