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: W) -> Self
123    where
124        W: Meta,
125    {
126        if !child.is_void() {
127            self.keys.push(key);
128            self.children.push(child);
129        }
130
131        self
132    }
133
134    /// Adds an element to the [`Column`], if `Some`.
135    pub fn push_maybe(self, key: Key, child: Option<W>) -> Self
136    where
137        W: Meta,
138    {
139        if let Some(child) = child {
140            self.push(key, child)
141        } else {
142            self
143        }
144    }
145
146    /// Extends the [`Column`] with the given children.
147    pub fn extend(self, children: impl IntoIterator<Item = (Key, W)>) -> Self
148    where
149        W: Meta,
150    {
151        children
152            .into_iter()
153            .fold(self, |column, (key, child)| column.push(key, child))
154    }
155}
156
157impl<Key, W> Default for Column<Key, W>
158where
159    Key: Copy + PartialEq,
160{
161    fn default() -> Self {
162        Self::new()
163    }
164}
165
166struct State<Key>
167where
168    Key: Copy + PartialEq,
169{
170    keys: Vec<Key>,
171    cache: layout::flex::Cache,
172}
173
174impl<Key, W> Meta for Column<Key, W> where Key: Copy + PartialEq {}
175
176impl<Key, W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Column<Key, W>
177where
178    Key: Copy + PartialEq + 'static,
179    W: Widget<Message, Theme, Renderer>,
180{
181    fn tag(&self) -> tree::Tag {
182        tree::Tag::of::<State<Key>>()
183    }
184
185    fn state(&self) -> tree::State {
186        tree::State::new(State {
187            keys: self.keys.clone(),
188            cache: layout::flex::Cache::default(),
189        })
190    }
191
192    fn diff(&mut self, tree: &mut Tree) {
193        let Tree {
194            state, children, ..
195        } = tree;
196
197        let state = state.downcast_mut::<State<Key>>();
198
199        tree::diff_children_custom_with_search(
200            children,
201            &mut self.children,
202            Tree::diff,
203            |index| {
204                self.keys.get(index).or_else(|| self.keys.last()).copied()
205                    != Some(state.keys[index])
206            },
207            |child| Tree::new(child),
208        );
209
210        if state.keys != self.keys {
211            state.keys.clone_from(&self.keys);
212        }
213
214        if self.width.is_fit() || self.height.is_fit() {
215            for child in &self.children {
216                let size = child.size();
217
218                self.width = self.width.cross(size.width);
219                self.height = self.height.stack(size.height);
220            }
221        }
222    }
223
224    fn size(&self) -> Size<Length> {
225        Size {
226            width: self.width,
227            height: self.height,
228        }
229    }
230
231    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
232        let state = tree.state.downcast_mut::<State<Key>>();
233
234        tree.size = layout::flex::resolve(
235            layout::flex::Axis::Vertical,
236            renderer,
237            limits,
238            self.width,
239            self.height,
240            self.padding,
241            self.spacing,
242            self.align_items,
243            &mut tree.children,
244            &mut self.children,
245            &mut state.cache,
246        );
247    }
248
249    fn operate(
250        &mut self,
251        tree: &mut Tree,
252        layout: Layout,
253        viewport: &Rectangle,
254        renderer: &Renderer,
255        operation: &mut dyn Operation,
256    ) {
257        operation.container(None, layout.bounds(), viewport);
258        operation.traverse(&mut |operation| {
259            self.children
260                .iter_mut()
261                .zip(layout.iter_mut(&mut tree.children))
262                .for_each(|(child, (layout, state))| {
263                    child.operate(state, layout, viewport, renderer, operation);
264                });
265        });
266    }
267
268    fn update(
269        &mut self,
270        tree: &mut Tree,
271        event: &Event,
272        layout: Layout,
273        cursor: mouse::Cursor,
274        renderer: &Renderer,
275        shell: &mut Shell<'_, Message>,
276        viewport: &Rectangle,
277    ) {
278        for (child, (layout, tree)) in self
279            .children
280            .iter_mut()
281            .zip(layout.iter_mut(&mut tree.children))
282        {
283            child.update(tree, event, layout, cursor, renderer, shell, viewport);
284        }
285    }
286
287    fn mouse_interaction(
288        &self,
289        tree: &Tree,
290        layout: Layout,
291        cursor: mouse::Cursor,
292        viewport: &Rectangle,
293        renderer: &Renderer,
294    ) -> mouse::Interaction {
295        self.children
296            .iter()
297            .zip(layout.iter(&tree.children))
298            .map(|(child, (layout, tree))| {
299                child.mouse_interaction(tree, layout, cursor, viewport, renderer)
300            })
301            .max()
302            .unwrap_or_default()
303    }
304
305    fn draw(
306        &self,
307        tree: &Tree,
308        renderer: &mut Renderer,
309        theme: &Theme,
310        style: &renderer::Style,
311        layout: Layout,
312        cursor: mouse::Cursor,
313        viewport: &Rectangle,
314    ) {
315        for (child, (layout, state)) in self.children.iter().zip(layout.iter(&tree.children)) {
316            child.draw(state, renderer, theme, style, layout, cursor, viewport);
317        }
318    }
319
320    fn overlay<'b>(
321        &'b mut self,
322        tree: &'b mut Tree,
323        layout: Layout,
324        renderer: &Renderer,
325        viewport: &Rectangle,
326        translation: Vector,
327        window: Size,
328    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
329        overlay::from_children(
330            &mut self.children,
331            tree,
332            layout,
333            renderer,
334            viewport,
335            translation,
336            window,
337        )
338    }
339}