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: W) -> Self
74    where
75        W: Meta,
76    {
77        if !child.is_void() {
78            self.children.push(child);
79        }
80
81        self
82    }
83
84    /// Adds a widget under the [`Stack`].
85    pub fn push_under(mut self, child: W) -> Self {
86        self.children.insert(0, child);
87        self.base_layer += 1;
88        self
89    }
90
91    /// Extends the [`Stack`] with the given children.
92    pub fn extend(self, children: impl IntoIterator<Item = W>) -> Self
93    where
94        W: Meta,
95    {
96        children.into_iter().fold(self, Self::push)
97    }
98
99    /// Sets whether the [`Stack`] should clip overflowing content.
100    ///
101    /// It has a slight performance overhead during presentation.
102    ///
103    /// By default, it is set to `false`.
104    pub fn clip(mut self, clip: bool) -> Self {
105        self.clip = clip;
106        self
107    }
108}
109
110impl<W> Default for Stack<W> {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116impl<W> Meta for Stack<W> {}
117
118impl<W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Stack<W>
119where
120    W: Widget<Message, Theme, Renderer>,
121    Renderer: crate::core::Renderer,
122{
123    fn diff(&mut self, tree: &mut Tree) {
124        tree.diff_children(&mut self.children);
125
126        if let Some(base) = self.children.get(self.base_layer) {
127            let size = base.size();
128
129            self.width = self.width.cross(size.width);
130            self.height = self.height.cross(size.height);
131        }
132    }
133
134    fn size(&self) -> Size<Length> {
135        Size {
136            width: self.width,
137            height: self.height,
138        }
139    }
140
141    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
142        let limits = limits.width(self.width).height(self.height);
143
144        if self.children.len() <= self.base_layer {
145            tree.size = limits.resolve(self.width, self.height, Size::ZERO);
146            return;
147        }
148
149        self.children[self.base_layer].layout(
150            &mut tree.children[self.base_layer],
151            renderer,
152            &limits,
153        );
154
155        let size = limits.resolve(self.width, self.height, tree.children[self.base_layer].size);
156        let limits = layout::Limits::new(Size::ZERO, size);
157
158        let (under, above) = self.children.split_at_mut(self.base_layer);
159        let (tree_under, tree_above) = tree.children.split_at_mut(self.base_layer);
160
161        under.iter_mut().zip(tree_under).for_each(|(layer, tree)| {
162            layer.layout(tree, renderer, &limits);
163        });
164
165        above[1..]
166            .iter_mut()
167            .zip(&mut tree_above[1..])
168            .for_each(|(layer, tree)| layer.layout(tree, renderer, &limits));
169
170        tree.size = size;
171    }
172
173    fn operate(
174        &mut self,
175        tree: &mut Tree,
176        layout: Layout,
177        viewport: &Rectangle,
178        renderer: &Renderer,
179        operation: &mut dyn Operation,
180    ) {
181        operation.container(None, layout.bounds(), viewport);
182        operation.traverse(&mut |operation| {
183            self.children
184                .iter_mut()
185                .zip(layout.iter_mut(&mut tree.children))
186                .for_each(|(child, (layout, state))| {
187                    child.operate(state, layout, viewport, renderer, operation);
188                });
189        });
190    }
191
192    fn update(
193        &mut self,
194        tree: &mut Tree,
195        event: &Event,
196        layout: Layout,
197        mut cursor: mouse::Cursor,
198        renderer: &Renderer,
199        shell: &mut Shell<'_, Message>,
200        viewport: &Rectangle,
201    ) {
202        if self.children.is_empty() {
203            return;
204        }
205
206        let is_over = cursor.is_over(layout.bounds());
207        let end = self.children.len() - 1;
208
209        for (i, (child, (layout, tree))) in self
210            .children
211            .iter_mut()
212            .rev()
213            .zip(layout.iter_mut(&mut tree.children).rev())
214            .enumerate()
215        {
216            child.update(tree, event, layout, cursor, renderer, shell, viewport);
217
218            if shell.is_event_captured() {
219                return;
220            }
221
222            if i < end && is_over && !cursor.is_levitating() {
223                let interaction = child.mouse_interaction(tree, layout, cursor, viewport, renderer);
224
225                if interaction != mouse::Interaction::None {
226                    cursor = cursor.levitate();
227                }
228            }
229        }
230    }
231
232    fn mouse_interaction(
233        &self,
234        tree: &Tree,
235        layout: Layout,
236        cursor: mouse::Cursor,
237        viewport: &Rectangle,
238        renderer: &Renderer,
239    ) -> mouse::Interaction {
240        self.children
241            .iter()
242            .rev()
243            .zip(layout.iter(&tree.children).rev())
244            .map(|(child, (layout, tree))| {
245                child.mouse_interaction(tree, layout, cursor, viewport, renderer)
246            })
247            .find(|&interaction| interaction != mouse::Interaction::None)
248            .unwrap_or_default()
249    }
250
251    fn draw(
252        &self,
253        tree: &Tree,
254        renderer: &mut Renderer,
255        theme: &Theme,
256        style: &renderer::Style,
257        layout: Layout,
258        cursor: mouse::Cursor,
259        viewport: &Rectangle,
260    ) {
261        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
262            let viewport = if self.clip {
263                &clipped_viewport
264            } else {
265                viewport
266            };
267
268            let layers_under = if cursor.is_over(layout.bounds()) {
269                self.children
270                    .iter()
271                    .rev()
272                    .zip(layout.iter(&tree.children).rev())
273                    .position(|(layer, (layout, tree))| {
274                        let interaction =
275                            layer.mouse_interaction(tree, layout, cursor, viewport, renderer);
276
277                        interaction != mouse::Interaction::None
278                    })
279                    .map(|i| self.children.len() - i - 1)
280                    .unwrap_or_default()
281            } else {
282                0
283            };
284
285            let mut layers = self
286                .children
287                .iter()
288                .zip(layout.iter(&tree.children))
289                .enumerate();
290
291            let layers = layers.by_ref();
292
293            let mut draw_layer = |i, layer: &W, tree, layout, cursor| {
294                if i > 0 {
295                    renderer.with_layer(*viewport, |renderer| {
296                        layer.draw(tree, renderer, theme, style, layout, cursor, viewport);
297                    });
298                } else {
299                    layer.draw(tree, renderer, theme, style, layout, cursor, viewport);
300                }
301            };
302
303            for (i, (layer, (layout, tree))) in layers.take(layers_under) {
304                draw_layer(i, layer, tree, layout, mouse::Cursor::Unavailable);
305            }
306
307            for (i, (layer, (layout, tree))) in layers {
308                draw_layer(i, layer, tree, layout, cursor);
309            }
310        }
311    }
312
313    fn overlay<'b>(
314        &'b mut self,
315        tree: &'b mut Tree,
316        layout: Layout,
317        renderer: &Renderer,
318        viewport: &Rectangle,
319        translation: Vector,
320        window: Size,
321    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
322        overlay::from_children(
323            &mut self.children,
324            tree,
325            layout,
326            renderer,
327            viewport,
328            translation,
329            window,
330        )
331    }
332}