Skip to main content

iced_widget/keyed/
column.rs

1//! Keyed columns distribute content vertically while keeping continuity.
2use crate::core::layout;
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::widget::Operation;
7use crate::core::widget::tree::{self, Tree};
8use crate::core::{
9    Alignment, Element, Event, Layout, Length, Padding, Pixels, Rectangle, Shell, Size, Vector,
10    Widget,
11};
12
13/// A container that distributes its contents vertically while keeping continuity.
14///
15/// # Example
16/// ```no_run
17/// # mod iced { pub mod widget { pub use iced_widget::*; } }
18/// # pub type State = ();
19/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
20/// use iced::widget::{keyed_column, text};
21///
22/// enum Message {
23///     // ...
24/// }
25///
26/// fn view(state: &State) -> Element<'_, Message> {
27///     keyed_column((0..=100).map(|i| {
28///         (i, text!("Item {i}").into())
29///     })).into()
30/// }
31/// ```
32pub struct Column<'a, Key, Message, Theme = crate::Theme, Renderer = crate::Renderer>
33where
34    Key: Copy + PartialEq,
35{
36    spacing: f32,
37    padding: Padding,
38    width: Length,
39    height: Length,
40    align_items: Alignment,
41    keys: Vec<Key>,
42    children: Vec<Element<'a, Message, Theme, Renderer>>,
43}
44
45impl<'a, Key, Message, Theme, Renderer> Column<'a, Key, Message, Theme, Renderer>
46where
47    Key: Copy + PartialEq,
48    Renderer: crate::core::Renderer,
49{
50    /// Creates an empty [`Column`].
51    pub fn new() -> Self {
52        Self::from_vecs(Vec::new(), Vec::new())
53    }
54
55    /// Creates a [`Column`] from already allocated [`Vec`]s.
56    ///
57    /// Keep in mind that the [`Column`] will not inspect the [`Vec`]s, which means
58    /// it won't automatically adapt to the sizing strategy of its contents.
59    ///
60    /// If any of the children have a [`Length::Fill`] strategy, you will need to
61    /// call [`Column::width`] or [`Column::height`] accordingly.
62    pub fn from_vecs(keys: Vec<Key>, children: Vec<Element<'a, Message, Theme, Renderer>>) -> Self {
63        Self {
64            spacing: 0.0,
65            padding: Padding::ZERO,
66            width: Length::Fit,
67            height: Length::Fit,
68            align_items: Alignment::Start,
69            keys,
70            children,
71        }
72    }
73
74    /// Creates a [`Column`] with the given capacity.
75    pub fn with_capacity(capacity: usize) -> Self {
76        Self::from_vecs(Vec::with_capacity(capacity), Vec::with_capacity(capacity))
77    }
78
79    /// Creates a [`Column`] with the given elements.
80    pub fn with_children(
81        children: impl IntoIterator<Item = (Key, Element<'a, Message, Theme, Renderer>)>,
82    ) -> Self {
83        let iterator = children.into_iter();
84
85        Self::with_capacity(iterator.size_hint().0).extend(iterator)
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 horizontal alignment of the contents of the [`Column`] .
117    pub fn align_items(mut self, align: Alignment) -> Self {
118        self.align_items = align;
119        self
120    }
121
122    /// Adds an element to the [`Column`].
123    pub fn push(
124        mut self,
125        key: Key,
126        child: impl Into<Element<'a, Message, Theme, Renderer>>,
127    ) -> Self {
128        let child = child.into();
129
130        if !child.as_widget().is_void() {
131            self.keys.push(key);
132            self.children.push(child);
133        }
134
135        self
136    }
137
138    /// Adds an element to the [`Column`], if `Some`.
139    pub fn push_maybe(
140        self,
141        key: Key,
142        child: Option<impl Into<Element<'a, Message, Theme, Renderer>>>,
143    ) -> Self {
144        if let Some(child) = child {
145            self.push(key, child)
146        } else {
147            self
148        }
149    }
150
151    /// Extends the [`Column`] with the given children.
152    pub fn extend(
153        self,
154        children: impl IntoIterator<Item = (Key, Element<'a, Message, Theme, Renderer>)>,
155    ) -> Self {
156        children
157            .into_iter()
158            .fold(self, |column, (key, child)| column.push(key, child))
159    }
160}
161
162impl<Key, Message, Renderer> Default for Column<'_, Key, Message, Renderer>
163where
164    Key: Copy + PartialEq,
165    Renderer: crate::core::Renderer,
166{
167    fn default() -> Self {
168        Self::new()
169    }
170}
171
172struct State<Key>
173where
174    Key: Copy + PartialEq,
175{
176    keys: Vec<Key>,
177}
178
179impl<Key, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
180    for Column<'_, Key, Message, Theme, Renderer>
181where
182    Renderer: crate::core::Renderer,
183    Key: Copy + PartialEq + 'static,
184{
185    fn tag(&self) -> tree::Tag {
186        tree::Tag::of::<State<Key>>()
187    }
188
189    fn state(&self) -> tree::State {
190        tree::State::new(State {
191            keys: self.keys.clone(),
192        })
193    }
194
195    fn diff(&mut self, tree: &mut Tree) {
196        let Tree {
197            state, children, ..
198        } = tree;
199
200        let state = state.downcast_mut::<State<Key>>();
201
202        tree::diff_children_custom_with_search(
203            children,
204            &mut self.children,
205            |tree, child| child.as_widget_mut().diff(tree),
206            |index| {
207                self.keys.get(index).or_else(|| self.keys.last()).copied()
208                    != Some(state.keys[index])
209            },
210            |child| Tree::new(child.as_widget()),
211        );
212
213        if state.keys != self.keys {
214            state.keys.clone_from(&self.keys);
215        }
216
217        if self.width.is_fit() || self.height.is_fit() {
218            for child in &self.children {
219                let size = child.as_widget().size();
220
221                self.width = self.width.cross(size.width);
222                self.height = self.height.stack(size.height);
223            }
224        }
225    }
226
227    fn size(&self) -> Size<Length> {
228        Size {
229            width: self.width,
230            height: self.height,
231        }
232    }
233
234    fn layout(
235        &mut self,
236        tree: &mut Tree,
237        renderer: &Renderer,
238        limits: &layout::Limits,
239    ) -> layout::Node {
240        layout::flex::resolve(
241            layout::flex::Axis::Vertical,
242            renderer,
243            limits,
244            self.width,
245            self.height,
246            self.padding,
247            self.spacing,
248            self.align_items,
249            &mut self.children,
250            &mut tree.children,
251        )
252    }
253
254    fn operate(
255        &mut self,
256        tree: &mut Tree,
257        layout: Layout<'_>,
258        renderer: &Renderer,
259        operation: &mut dyn Operation,
260    ) {
261        operation.container(None, layout.bounds());
262        operation.traverse(&mut |operation| {
263            self.children
264                .iter_mut()
265                .zip(&mut tree.children)
266                .zip(layout.children())
267                .for_each(|((child, state), layout)| {
268                    child
269                        .as_widget_mut()
270                        .operate(state, layout, renderer, operation);
271                });
272        });
273    }
274
275    fn update(
276        &mut self,
277        tree: &mut Tree,
278        event: &Event,
279        layout: Layout<'_>,
280        cursor: mouse::Cursor,
281        renderer: &Renderer,
282        shell: &mut Shell<'_, Message>,
283        viewport: &Rectangle,
284    ) {
285        for ((child, tree), layout) in self
286            .children
287            .iter_mut()
288            .zip(&mut tree.children)
289            .zip(layout.children())
290        {
291            child
292                .as_widget_mut()
293                .update(tree, event, layout, cursor, renderer, shell, viewport);
294        }
295    }
296
297    fn mouse_interaction(
298        &self,
299        tree: &Tree,
300        layout: Layout<'_>,
301        cursor: mouse::Cursor,
302        viewport: &Rectangle,
303        renderer: &Renderer,
304    ) -> mouse::Interaction {
305        self.children
306            .iter()
307            .zip(&tree.children)
308            .zip(layout.children())
309            .map(|((child, tree), layout)| {
310                child
311                    .as_widget()
312                    .mouse_interaction(tree, layout, cursor, viewport, renderer)
313            })
314            .max()
315            .unwrap_or_default()
316    }
317
318    fn draw(
319        &self,
320        tree: &Tree,
321        renderer: &mut Renderer,
322        theme: &Theme,
323        style: &renderer::Style,
324        layout: Layout<'_>,
325        cursor: mouse::Cursor,
326        viewport: &Rectangle,
327    ) {
328        for ((child, state), layout) in self
329            .children
330            .iter()
331            .zip(&tree.children)
332            .zip(layout.children())
333        {
334            child
335                .as_widget()
336                .draw(state, renderer, theme, style, layout, cursor, viewport);
337        }
338    }
339
340    fn overlay<'b>(
341        &'b mut self,
342        tree: &'b mut Tree,
343        layout: Layout<'b>,
344        renderer: &Renderer,
345        viewport: &Rectangle,
346        translation: Vector,
347    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
348        overlay::from_children(
349            &mut self.children,
350            tree,
351            layout,
352            renderer,
353            viewport,
354            translation,
355        )
356    }
357}
358
359impl<'a, Key, Message, Theme, Renderer> From<Column<'a, Key, Message, Theme, Renderer>>
360    for Element<'a, Message, Theme, Renderer>
361where
362    Key: Copy + PartialEq + 'static,
363    Message: 'a,
364    Theme: 'a,
365    Renderer: crate::core::Renderer + 'a,
366{
367    fn from(column: Column<'a, Key, Message, Theme, Renderer>) -> Self {
368        Self::new(column)
369    }
370}