Skip to main content

iced_widget/
stack.rs

1//! Display content on top of other content.
2use crate::core::layout;
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::widget::{Meta, Operation, Tree};
7use crate::core::{Event, Layout, Length, Rectangle, Shell, Size, Vector, Widget};
8
9/// A container that displays children on top of each other.
10///
11/// The first [`Widget`] dictates the intrinsic [`Size`] of a [`Stack`] and
12/// will be displayed as the base layer. Every consecutive [`Widget`] will be
13/// rendered on top; on its own layer.
14///
15/// You can use [`push_under`](Self::push_under) to push a [`Widget`] under
16/// the current [`Stack`] without affecting its intrinsic [`Size`].
17///
18/// Keep in mind that too much layering will normally produce bad UX as well as
19/// introduce certain rendering overhead. Use this widget sparingly!
20pub struct Stack<W> {
21    width: Length,
22    height: Length,
23    children: Vec<W>,
24    clip: bool,
25    base_layer: usize,
26}
27
28impl<W> Stack<W> {
29    /// Creates an empty [`Stack`].
30    pub fn new() -> Self {
31        Self::from_vec(Vec::new())
32    }
33
34    /// Creates a [`Stack`] with the given capacity.
35    pub fn with_capacity(capacity: usize) -> Self {
36        Self::from_vec(Vec::with_capacity(capacity))
37    }
38
39    /// Creates a [`Stack`] with the given widgets.
40    pub fn with_children(children: impl IntoIterator<Item = W>) -> Self
41    where
42        W: Meta,
43    {
44        let iterator = children.into_iter();
45
46        Self::with_capacity(iterator.size_hint().0).extend(iterator)
47    }
48
49    /// Creates a [`Stack`] from an already allocated [`Vec`].
50    pub fn from_vec(children: Vec<W>) -> Self {
51        Self {
52            width: Length::Fit,
53            height: Length::Fit,
54            children,
55            clip: false,
56            base_layer: 0,
57        }
58    }
59
60    /// Sets the width of the [`Stack`].
61    pub fn width(mut self, width: impl Into<Length>) -> Self {
62        self.width = width.into();
63        self
64    }
65
66    /// Sets the height of the [`Stack`].
67    pub fn height(mut self, height: impl Into<Length>) -> Self {
68        self.height = height.into();
69        self
70    }
71
72    /// Adds a widget on top of the [`Stack`].
73    pub fn push(mut self, child: impl Into<W>) -> Self
74    where
75        W: Meta,
76    {
77        let child = child.into();
78
79        if !child.is_void() {
80            self.children.push(child);
81        }
82
83        self
84    }
85
86    /// Adds a widget under the [`Stack`].
87    pub fn push_under(mut self, child: impl Into<W>) -> Self {
88        self.children.insert(0, child.into());
89        self.base_layer += 1;
90        self
91    }
92
93    /// Extends the [`Stack`] with the given children.
94    pub fn extend(self, children: impl IntoIterator<Item = W>) -> Self
95    where
96        W: Meta,
97    {
98        children.into_iter().fold(self, Self::push)
99    }
100
101    /// Sets whether the [`Stack`] should clip overflowing content.
102    ///
103    /// It has a slight performance overhead during presentation.
104    ///
105    /// By default, it is set to `false`.
106    pub fn clip(mut self, clip: bool) -> Self {
107        self.clip = clip;
108        self
109    }
110}
111
112impl<W> Default for Stack<W> {
113    fn default() -> Self {
114        Self::new()
115    }
116}
117
118impl<W> Meta for Stack<W> {}
119
120impl<W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Stack<W>
121where
122    W: Widget<Message, Theme, Renderer>,
123    Renderer: crate::core::Renderer,
124{
125    fn diff(&mut self, tree: &mut Tree) {
126        tree.diff_children(&mut self.children);
127
128        if let Some(base) = self.children.get(self.base_layer) {
129            let size = base.size();
130
131            self.width = self.width.cross(size.width);
132            self.height = self.height.cross(size.height);
133        }
134    }
135
136    fn size(&self) -> Size<Length> {
137        Size {
138            width: self.width,
139            height: self.height,
140        }
141    }
142
143    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
144        let limits = limits.width(self.width).height(self.height);
145
146        if self.children.len() <= self.base_layer {
147            tree.size = limits.resolve(self.width, self.height, Size::ZERO);
148            return;
149        }
150
151        self.children[self.base_layer].layout(
152            &mut tree.children[self.base_layer],
153            renderer,
154            &limits,
155        );
156
157        let size = limits.resolve(self.width, self.height, tree.children[self.base_layer].size);
158        let limits = layout::Limits::new(Size::ZERO, size);
159
160        let (under, above) = self.children.split_at_mut(self.base_layer);
161        let (tree_under, tree_above) = tree.children.split_at_mut(self.base_layer);
162
163        under.iter_mut().zip(tree_under).for_each(|(layer, tree)| {
164            layer.layout(tree, renderer, &limits);
165        });
166
167        above[1..]
168            .iter_mut()
169            .zip(&mut tree_above[1..])
170            .for_each(|(layer, tree)| layer.layout(tree, renderer, &limits));
171
172        tree.size = size;
173    }
174
175    fn operate(
176        &mut self,
177        tree: &mut Tree,
178        layout: Layout,
179        viewport: &Rectangle,
180        renderer: &Renderer,
181        operation: &mut dyn Operation,
182    ) {
183        operation.container(None, layout.bounds(), viewport);
184        operation.traverse(&mut |operation| {
185            self.children
186                .iter_mut()
187                .zip(layout.iter_mut(&mut tree.children))
188                .for_each(|(child, (layout, state))| {
189                    child.operate(state, layout, viewport, renderer, operation);
190                });
191        });
192    }
193
194    fn update(
195        &mut self,
196        tree: &mut Tree,
197        event: &Event,
198        layout: Layout,
199        mut cursor: mouse::Cursor,
200        renderer: &Renderer,
201        shell: &mut Shell<'_, Message>,
202        viewport: &Rectangle,
203    ) {
204        if self.children.is_empty() {
205            return;
206        }
207
208        let is_over = cursor.is_over(layout.bounds());
209        let end = self.children.len() - 1;
210
211        for (i, (child, (layout, tree))) in self
212            .children
213            .iter_mut()
214            .rev()
215            .zip(layout.iter_mut(&mut tree.children).rev())
216            .enumerate()
217        {
218            child.update(tree, event, layout, cursor, renderer, shell, viewport);
219
220            if shell.is_event_captured() {
221                return;
222            }
223
224            if i < end && is_over && !cursor.is_levitating() {
225                let interaction = child.mouse_interaction(tree, layout, cursor, viewport, renderer);
226
227                if interaction != mouse::Interaction::None {
228                    cursor = cursor.levitate();
229                }
230            }
231        }
232    }
233
234    fn mouse_interaction(
235        &self,
236        tree: &Tree,
237        layout: Layout,
238        cursor: mouse::Cursor,
239        viewport: &Rectangle,
240        renderer: &Renderer,
241    ) -> mouse::Interaction {
242        self.children
243            .iter()
244            .rev()
245            .zip(layout.iter(&tree.children).rev())
246            .map(|(child, (layout, tree))| {
247                child.mouse_interaction(tree, layout, cursor, viewport, renderer)
248            })
249            .find(|&interaction| interaction != mouse::Interaction::None)
250            .unwrap_or_default()
251    }
252
253    fn draw(
254        &self,
255        tree: &Tree,
256        renderer: &mut Renderer,
257        theme: &Theme,
258        style: &renderer::Style,
259        layout: Layout,
260        cursor: mouse::Cursor,
261        viewport: &Rectangle,
262    ) {
263        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
264            let viewport = if self.clip {
265                &clipped_viewport
266            } else {
267                viewport
268            };
269
270            let layers_under = if cursor.is_over(layout.bounds()) {
271                self.children
272                    .iter()
273                    .rev()
274                    .zip(layout.iter(&tree.children).rev())
275                    .position(|(layer, (layout, tree))| {
276                        let interaction =
277                            layer.mouse_interaction(tree, layout, cursor, viewport, renderer);
278
279                        interaction != mouse::Interaction::None
280                    })
281                    .map(|i| self.children.len() - i - 1)
282                    .unwrap_or_default()
283            } else {
284                0
285            };
286
287            let mut layers = self
288                .children
289                .iter()
290                .zip(layout.iter(&tree.children))
291                .enumerate();
292
293            let layers = layers.by_ref();
294
295            let mut draw_layer = |i, layer: &W, tree, layout, cursor| {
296                if i > 0 {
297                    renderer.with_layer(*viewport, |renderer| {
298                        layer.draw(tree, renderer, theme, style, layout, cursor, viewport);
299                    });
300                } else {
301                    layer.draw(tree, renderer, theme, style, layout, cursor, viewport);
302                }
303            };
304
305            for (i, (layer, (layout, tree))) in layers.take(layers_under) {
306                draw_layer(i, layer, tree, layout, mouse::Cursor::Unavailable);
307            }
308
309            for (i, (layer, (layout, tree))) in layers {
310                draw_layer(i, layer, tree, layout, cursor);
311            }
312        }
313    }
314
315    fn overlay<'b>(
316        &'b mut self,
317        tree: &'b mut Tree,
318        layout: Layout,
319        renderer: &Renderer,
320        viewport: &Rectangle,
321        translation: Vector,
322        window: Size,
323    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
324        overlay::from_children(
325            &mut self.children,
326            tree,
327            layout,
328            renderer,
329            viewport,
330            translation,
331            window,
332        )
333    }
334}