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    max_width: f32,
41    align_items: Alignment,
42    keys: Vec<Key>,
43    children: Vec<Element<'a, Message, Theme, Renderer>>,
44}
45
46impl<'a, Key, Message, Theme, Renderer> Column<'a, Key, Message, Theme, Renderer>
47where
48    Key: Copy + PartialEq,
49    Renderer: crate::core::Renderer,
50{
51    /// Creates an empty [`Column`].
52    pub fn new() -> Self {
53        Self::from_vecs(Vec::new(), Vec::new())
54    }
55
56    /// Creates a [`Column`] from already allocated [`Vec`]s.
57    ///
58    /// Keep in mind that the [`Column`] will not inspect the [`Vec`]s, which means
59    /// it won't automatically adapt to the sizing strategy of its contents.
60    ///
61    /// If any of the children have a [`Length::Fill`] strategy, you will need to
62    /// call [`Column::width`] or [`Column::height`] accordingly.
63    pub fn from_vecs(keys: Vec<Key>, children: Vec<Element<'a, Message, Theme, Renderer>>) -> Self {
64        Self {
65            spacing: 0.0,
66            padding: Padding::ZERO,
67            width: Length::Fit,
68            height: Length::Fit,
69            max_width: f32::INFINITY,
70            align_items: Alignment::Start,
71            keys,
72            children,
73        }
74    }
75
76    /// Creates a [`Column`] with the given capacity.
77    pub fn with_capacity(capacity: usize) -> Self {
78        Self::from_vecs(Vec::with_capacity(capacity), Vec::with_capacity(capacity))
79    }
80
81    /// Creates a [`Column`] with the given elements.
82    pub fn with_children(
83        children: impl IntoIterator<Item = (Key, Element<'a, Message, Theme, Renderer>)>,
84    ) -> Self {
85        let iterator = children.into_iter();
86
87        Self::with_capacity(iterator.size_hint().0).extend(iterator)
88    }
89
90    /// Sets the vertical spacing _between_ elements.
91    ///
92    /// Custom margins per element do not exist in iced. You should use this
93    /// method instead! While less flexible, it helps you keep spacing between
94    /// elements consistent.
95    pub fn spacing(mut self, amount: impl Into<Pixels>) -> Self {
96        self.spacing = amount.into().0;
97        self
98    }
99
100    /// Sets the [`Padding`] of the [`Column`].
101    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
102        self.padding = padding.into();
103        self
104    }
105
106    /// Sets the width of the [`Column`].
107    pub fn width(mut self, width: impl Into<Length>) -> Self {
108        self.width = width.into();
109        self
110    }
111
112    /// Sets the height of the [`Column`].
113    pub fn height(mut self, height: impl Into<Length>) -> Self {
114        self.height = height.into();
115        self
116    }
117
118    /// Sets the maximum width of the [`Column`].
119    pub fn max_width(mut self, max_width: impl Into<Pixels>) -> Self {
120        self.max_width = max_width.into().0;
121        self
122    }
123
124    /// Sets the horizontal alignment of the contents of the [`Column`] .
125    pub fn align_items(mut self, align: Alignment) -> Self {
126        self.align_items = align;
127        self
128    }
129
130    /// Adds an element to the [`Column`].
131    pub fn push(
132        mut self,
133        key: Key,
134        child: impl Into<Element<'a, Message, Theme, Renderer>>,
135    ) -> Self {
136        let child = child.into();
137
138        if !child.as_widget().size().is_void() {
139            self.keys.push(key);
140            self.children.push(child);
141        }
142
143        self
144    }
145
146    /// Adds an element to the [`Column`], if `Some`.
147    pub fn push_maybe(
148        self,
149        key: Key,
150        child: Option<impl Into<Element<'a, Message, Theme, Renderer>>>,
151    ) -> Self {
152        if let Some(child) = child {
153            self.push(key, child)
154        } else {
155            self
156        }
157    }
158
159    /// Extends the [`Column`] with the given children.
160    pub fn extend(
161        self,
162        children: impl IntoIterator<Item = (Key, Element<'a, Message, Theme, Renderer>)>,
163    ) -> Self {
164        children
165            .into_iter()
166            .fold(self, |column, (key, child)| column.push(key, child))
167    }
168}
169
170impl<Key, Message, Renderer> Default for Column<'_, Key, Message, Renderer>
171where
172    Key: Copy + PartialEq,
173    Renderer: crate::core::Renderer,
174{
175    fn default() -> Self {
176        Self::new()
177    }
178}
179
180struct State<Key>
181where
182    Key: Copy + PartialEq,
183{
184    keys: Vec<Key>,
185}
186
187impl<Key, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
188    for Column<'_, Key, Message, Theme, Renderer>
189where
190    Renderer: crate::core::Renderer,
191    Key: Copy + PartialEq + 'static,
192{
193    fn tag(&self) -> tree::Tag {
194        tree::Tag::of::<State<Key>>()
195    }
196
197    fn state(&self) -> tree::State {
198        tree::State::new(State {
199            keys: self.keys.clone(),
200        })
201    }
202
203    fn diff(&mut self, tree: &mut Tree) {
204        let Tree {
205            state, children, ..
206        } = tree;
207
208        let state = state.downcast_mut::<State<Key>>();
209
210        tree::diff_children_custom_with_search(
211            children,
212            &mut self.children,
213            |tree, child| child.as_widget_mut().diff(tree),
214            |index| {
215                self.keys.get(index).or_else(|| self.keys.last()).copied()
216                    != Some(state.keys[index])
217            },
218            |child| Tree::new(child.as_widget()),
219        );
220
221        if state.keys != self.keys {
222            state.keys.clone_from(&self.keys);
223        }
224
225        if self.width.is_fit() || self.height.is_fit() {
226            for child in &self.children {
227                let size = child.as_widget().size();
228
229                self.width = self.width.enclose(size.width);
230                self.height = self.height.enclose(size.height);
231            }
232        }
233    }
234
235    fn size(&self) -> Size<Length> {
236        Size {
237            width: self.width,
238            height: self.height,
239        }
240    }
241
242    fn layout(
243        &mut self,
244        tree: &mut Tree,
245        renderer: &Renderer,
246        limits: &layout::Limits,
247    ) -> layout::Node {
248        let limits = limits
249            .max_width(self.max_width)
250            .width(self.width)
251            .height(self.height);
252
253        layout::flex::resolve(
254            layout::flex::Axis::Vertical,
255            renderer,
256            &limits,
257            self.width,
258            self.height,
259            self.padding,
260            self.spacing,
261            self.align_items,
262            &mut self.children,
263            &mut tree.children,
264        )
265    }
266
267    fn operate(
268        &mut self,
269        tree: &mut Tree,
270        layout: Layout<'_>,
271        renderer: &Renderer,
272        operation: &mut dyn Operation,
273    ) {
274        operation.container(None, layout.bounds());
275        operation.traverse(&mut |operation| {
276            self.children
277                .iter_mut()
278                .zip(&mut tree.children)
279                .zip(layout.children())
280                .for_each(|((child, state), layout)| {
281                    child
282                        .as_widget_mut()
283                        .operate(state, layout, renderer, operation);
284                });
285        });
286    }
287
288    fn update(
289        &mut self,
290        tree: &mut Tree,
291        event: &Event,
292        layout: Layout<'_>,
293        cursor: mouse::Cursor,
294        renderer: &Renderer,
295        shell: &mut Shell<'_, Message>,
296        viewport: &Rectangle,
297    ) {
298        for ((child, tree), layout) in self
299            .children
300            .iter_mut()
301            .zip(&mut tree.children)
302            .zip(layout.children())
303        {
304            child
305                .as_widget_mut()
306                .update(tree, event, layout, cursor, renderer, shell, viewport);
307        }
308    }
309
310    fn mouse_interaction(
311        &self,
312        tree: &Tree,
313        layout: Layout<'_>,
314        cursor: mouse::Cursor,
315        viewport: &Rectangle,
316        renderer: &Renderer,
317    ) -> mouse::Interaction {
318        self.children
319            .iter()
320            .zip(&tree.children)
321            .zip(layout.children())
322            .map(|((child, tree), layout)| {
323                child
324                    .as_widget()
325                    .mouse_interaction(tree, layout, cursor, viewport, renderer)
326            })
327            .max()
328            .unwrap_or_default()
329    }
330
331    fn draw(
332        &self,
333        tree: &Tree,
334        renderer: &mut Renderer,
335        theme: &Theme,
336        style: &renderer::Style,
337        layout: Layout<'_>,
338        cursor: mouse::Cursor,
339        viewport: &Rectangle,
340    ) {
341        for ((child, state), layout) in self
342            .children
343            .iter()
344            .zip(&tree.children)
345            .zip(layout.children())
346        {
347            child
348                .as_widget()
349                .draw(state, renderer, theme, style, layout, cursor, viewport);
350        }
351    }
352
353    fn overlay<'b>(
354        &'b mut self,
355        tree: &'b mut Tree,
356        layout: Layout<'b>,
357        renderer: &Renderer,
358        viewport: &Rectangle,
359        translation: Vector,
360    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
361        overlay::from_children(
362            &mut self.children,
363            tree,
364            layout,
365            renderer,
366            viewport,
367            translation,
368        )
369    }
370}
371
372impl<'a, Key, Message, Theme, Renderer> From<Column<'a, Key, Message, Theme, Renderer>>
373    for Element<'a, Message, Theme, Renderer>
374where
375    Key: Copy + PartialEq + 'static,
376    Message: 'a,
377    Theme: 'a,
378    Renderer: crate::core::Renderer + 'a,
379{
380    fn from(column: Column<'a, Key, Message, Theme, Renderer>) -> Self {
381        Self::new(column)
382    }
383}