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    Renderer: crate::core::Renderer,
179    Key: Copy + PartialEq + 'static,
180    W: Widget<Message, Theme, Renderer>,
181{
182    fn tag(&self) -> tree::Tag {
183        tree::Tag::of::<State<Key>>()
184    }
185
186    fn state(&self) -> tree::State {
187        tree::State::new(State {
188            keys: self.keys.clone(),
189            cache: layout::flex::Cache::default(),
190        })
191    }
192
193    fn diff(&mut self, tree: &mut Tree) {
194        let Tree {
195            state, children, ..
196        } = tree;
197
198        let state = state.downcast_mut::<State<Key>>();
199
200        tree::diff_children_custom_with_search(
201            children,
202            &mut self.children,
203            Tree::diff,
204            |index| {
205                self.keys.get(index).or_else(|| self.keys.last()).copied()
206                    != Some(state.keys[index])
207            },
208            |child| Tree::new(child),
209        );
210
211        if state.keys != self.keys {
212            state.keys.clone_from(&self.keys);
213        }
214
215        if self.width.is_fit() || self.height.is_fit() {
216            for child in &self.children {
217                let size = child.size();
218
219                self.width = self.width.cross(size.width);
220                self.height = self.height.stack(size.height);
221            }
222        }
223    }
224
225    fn size(&self) -> Size<Length> {
226        Size {
227            width: self.width,
228            height: self.height,
229        }
230    }
231
232    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
233        let state = tree.state.downcast_mut::<State<Key>>();
234
235        tree.size = layout::flex::resolve(
236            layout::flex::Axis::Vertical,
237            renderer,
238            limits,
239            self.width,
240            self.height,
241            self.padding,
242            self.spacing,
243            self.align_items,
244            &mut tree.children,
245            &mut self.children,
246            &mut state.cache,
247        );
248    }
249
250    fn operate(
251        &mut self,
252        tree: &mut Tree,
253        layout: Layout,
254        viewport: &Rectangle,
255        renderer: &Renderer,
256        operation: &mut dyn Operation,
257    ) {
258        operation.container(None, layout.bounds(), viewport);
259        operation.traverse(&mut |operation| {
260            self.children
261                .iter_mut()
262                .zip(layout.iter_mut(&mut tree.children))
263                .for_each(|(child, (layout, state))| {
264                    child.operate(state, layout, viewport, renderer, operation);
265                });
266        });
267    }
268
269    fn update(
270        &mut self,
271        tree: &mut Tree,
272        event: &Event,
273        layout: Layout,
274        cursor: mouse::Cursor,
275        renderer: &Renderer,
276        shell: &mut Shell<'_, Message>,
277        viewport: &Rectangle,
278    ) {
279        for (child, (layout, tree)) in self
280            .children
281            .iter_mut()
282            .zip(layout.iter_mut(&mut tree.children))
283        {
284            child.update(tree, event, layout, cursor, renderer, shell, viewport);
285        }
286    }
287
288    fn mouse_interaction(
289        &self,
290        tree: &Tree,
291        layout: Layout,
292        cursor: mouse::Cursor,
293        viewport: &Rectangle,
294        renderer: &Renderer,
295    ) -> mouse::Interaction {
296        self.children
297            .iter()
298            .zip(layout.iter(&tree.children))
299            .map(|(child, (layout, tree))| {
300                child.mouse_interaction(tree, layout, cursor, viewport, renderer)
301            })
302            .max()
303            .unwrap_or_default()
304    }
305
306    fn draw(
307        &self,
308        tree: &Tree,
309        renderer: &mut Renderer,
310        theme: &Theme,
311        style: &renderer::Style,
312        layout: Layout,
313        cursor: mouse::Cursor,
314        viewport: &Rectangle,
315    ) {
316        for (child, (layout, state)) in self.children.iter().zip(layout.iter(&tree.children)) {
317            child.draw(state, renderer, theme, style, layout, cursor, viewport);
318        }
319    }
320
321    fn overlay<'b>(
322        &'b mut self,
323        tree: &'b mut Tree,
324        layout: Layout,
325        renderer: &Renderer,
326        viewport: &Rectangle,
327        translation: Vector,
328        window: Size,
329    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
330        overlay::from_children(
331            &mut self.children,
332            tree,
333            layout,
334            renderer,
335            viewport,
336            translation,
337            window,
338        )
339    }
340}