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