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::tree::{self, Tree};
7use crate::core::widget::{Meta, Operation};
8use crate::core::{
9    Alignment, Event, Layout, Length, Padding, Pixels, Rectangle, Shell, Size, Vector, Widget,
10};
11
12/// A container that distributes its contents vertically while keeping continuity.
13///
14/// # Example
15/// ```no_run
16/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
17/// # use iced::widget::Widget;
18/// # pub type State = ();
19/// use iced::widget::{keyed_column, text};
20///
21/// enum Message {
22///     // ...
23/// }
24///
25/// fn view(state: &State) -> impl Widget<Message> {
26///     keyed_column((0..=100).map(|i| {
27///         (i, text!("Item {i}"))
28///     }))
29/// }
30/// ```
31pub struct Column<Key, W>
32where
33    Key: Copy + PartialEq,
34{
35    spacing: f32,
36    padding: Padding,
37    width: Length,
38    height: Length,
39    align_items: Alignment,
40    keys: Vec<Key>,
41    children: Vec<W>,
42}
43
44impl<Key, W> Column<Key, W>
45where
46    Key: Copy + PartialEq,
47{
48    /// Creates an empty [`Column`].
49    pub fn new() -> Self {
50        Self::from_vecs(Vec::new(), Vec::new())
51    }
52
53    /// Creates a [`Column`] from already allocated [`Vec`]s.
54    ///
55    /// Keep in mind that the [`Column`] will not inspect the [`Vec`]s, which means
56    /// it won't automatically adapt to the sizing strategy of its contents.
57    ///
58    /// If any of the children have a [`Length::Fill`] strategy, you will need to
59    /// call [`Column::width`] or [`Column::height`] accordingly.
60    pub fn from_vecs(keys: Vec<Key>, children: Vec<W>) -> Self {
61        Self {
62            spacing: 0.0,
63            padding: Padding::ZERO,
64            width: Length::Fit,
65            height: Length::Fit,
66            align_items: Alignment::Start,
67            keys,
68            children,
69        }
70    }
71
72    /// Creates a [`Column`] with the given capacity.
73    pub fn with_capacity(capacity: usize) -> Self {
74        Self::from_vecs(Vec::with_capacity(capacity), Vec::with_capacity(capacity))
75    }
76
77    /// Creates a [`Column`] with the given elements.
78    pub fn with_children(children: impl IntoIterator<Item = (Key, W)>) -> Self
79    where
80        W: Meta,
81    {
82        let iterator = children.into_iter();
83
84        Self::with_capacity(iterator.size_hint().0).extend(iterator)
85    }
86
87    /// Sets the vertical spacing _between_ elements.
88    ///
89    /// Custom margins per element do not exist in iced. You should use this
90    /// method instead! While less flexible, it helps you keep spacing between
91    /// elements consistent.
92    pub fn spacing(mut self, amount: impl Into<Pixels>) -> Self {
93        self.spacing = amount.into().0;
94        self
95    }
96
97    /// Sets the [`Padding`] of the [`Column`].
98    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
99        self.padding = padding.into();
100        self
101    }
102
103    /// Sets the width of the [`Column`].
104    pub fn width(mut self, width: impl Into<Length>) -> Self {
105        self.width = width.into();
106        self
107    }
108
109    /// Sets the height of the [`Column`].
110    pub fn height(mut self, height: impl Into<Length>) -> Self {
111        self.height = height.into();
112        self
113    }
114
115    /// Sets the horizontal alignment of the contents of the [`Column`] .
116    pub fn align_items(mut self, align: Alignment) -> Self {
117        self.align_items = align;
118        self
119    }
120
121    /// Adds an element to the [`Column`].
122    pub fn push(mut self, key: Key, child: impl Into<W>) -> Self
123    where
124        W: Meta,
125    {
126        let child = child.into();
127
128        if !child.is_void() {
129            self.keys.push(key);
130            self.children.push(child);
131        }
132
133        self
134    }
135
136    /// Adds an element to the [`Column`], if `Some`.
137    pub fn push_maybe(self, key: Key, child: Option<impl Into<W>>) -> Self
138    where
139        W: Meta,
140    {
141        if let Some(child) = child {
142            self.push(key, child)
143        } else {
144            self
145        }
146    }
147
148    /// Extends the [`Column`] with the given children.
149    pub fn extend(self, children: impl IntoIterator<Item = (Key, W)>) -> Self
150    where
151        W: Meta,
152    {
153        children
154            .into_iter()
155            .fold(self, |column, (key, child)| column.push(key, child))
156    }
157}
158
159impl<Key, W> Default for Column<Key, W>
160where
161    Key: Copy + PartialEq,
162{
163    fn default() -> Self {
164        Self::new()
165    }
166}
167
168struct State<Key>
169where
170    Key: Copy + PartialEq,
171{
172    keys: Vec<Key>,
173    cache: layout::flex::Cache,
174}
175
176impl<Key, W> Meta for Column<Key, W> where Key: Copy + PartialEq {}
177
178impl<Key, W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Column<Key, W>
179where
180    Renderer: crate::core::Renderer,
181    Key: Copy + PartialEq + 'static,
182    W: Widget<Message, Theme, Renderer>,
183{
184    fn tag(&self) -> tree::Tag {
185        tree::Tag::of::<State<Key>>()
186    }
187
188    fn state(&self) -> tree::State {
189        tree::State::new(State {
190            keys: self.keys.clone(),
191            cache: layout::flex::Cache::default(),
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::diff,
206            |index| {
207                self.keys.get(index).or_else(|| self.keys.last()).copied()
208                    != Some(state.keys[index])
209            },
210            |child| Tree::new(child),
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.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(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
235        let state = tree.state.downcast_mut::<State<Key>>();
236
237        tree.size = layout::flex::resolve(
238            layout::flex::Axis::Vertical,
239            renderer,
240            limits,
241            self.width,
242            self.height,
243            self.padding,
244            self.spacing,
245            self.align_items,
246            &mut tree.children,
247            &mut self.children,
248            &mut state.cache,
249        );
250    }
251
252    fn operate(
253        &mut self,
254        tree: &mut Tree,
255        layout: Layout,
256        viewport: &Rectangle,
257        renderer: &Renderer,
258        operation: &mut dyn Operation,
259    ) {
260        operation.container(None, layout.bounds(), viewport);
261        operation.traverse(&mut |operation| {
262            self.children
263                .iter_mut()
264                .zip(layout.iter_mut(&mut tree.children))
265                .for_each(|(child, (layout, state))| {
266                    child.operate(state, layout, viewport, renderer, operation);
267                });
268        });
269    }
270
271    fn update(
272        &mut self,
273        tree: &mut Tree,
274        event: &Event,
275        layout: Layout,
276        cursor: mouse::Cursor,
277        renderer: &Renderer,
278        shell: &mut Shell<'_, Message>,
279        viewport: &Rectangle,
280    ) {
281        for (child, (layout, tree)) in self
282            .children
283            .iter_mut()
284            .zip(layout.iter_mut(&mut tree.children))
285        {
286            child.update(tree, event, layout, cursor, renderer, shell, viewport);
287        }
288    }
289
290    fn mouse_interaction(
291        &self,
292        tree: &Tree,
293        layout: Layout,
294        cursor: mouse::Cursor,
295        viewport: &Rectangle,
296        renderer: &Renderer,
297    ) -> mouse::Interaction {
298        self.children
299            .iter()
300            .zip(layout.iter(&tree.children))
301            .map(|(child, (layout, tree))| {
302                child.mouse_interaction(tree, layout, cursor, viewport, renderer)
303            })
304            .max()
305            .unwrap_or_default()
306    }
307
308    fn draw(
309        &self,
310        tree: &Tree,
311        renderer: &mut Renderer,
312        theme: &Theme,
313        style: &renderer::Style,
314        layout: Layout,
315        cursor: mouse::Cursor,
316        viewport: &Rectangle,
317    ) {
318        for (child, (layout, state)) in self.children.iter().zip(layout.iter(&tree.children)) {
319            child.draw(state, renderer, theme, style, layout, cursor, viewport);
320        }
321    }
322
323    fn overlay<'b>(
324        &'b mut self,
325        tree: &'b mut Tree,
326        layout: Layout,
327        renderer: &Renderer,
328        viewport: &Rectangle,
329        translation: Vector,
330        window: Size,
331    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
332        overlay::from_children(
333            &mut self.children,
334            tree,
335            layout,
336            renderer,
337            viewport,
338            translation,
339            window,
340        )
341    }
342}