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::{Operation, Tree};
7use crate::core::{
8    Clipboard, Element, Event, Layout, Length, Rectangle, Shell, Size, Vector,
9    Widget,
10};
11
12/// A container that displays children on top of each other.
13///
14/// The first [`Element`] dictates the intrinsic [`Size`] of a [`Stack`] and
15/// will be displayed as the base layer. Every consecutive [`Element`] will be
16/// renderer on top; on its own layer.
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!
20#[allow(missing_debug_implementations)]
21pub struct Stack<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
22{
23    width: Length,
24    height: Length,
25    children: Vec<Element<'a, Message, Theme, Renderer>>,
26    clip: bool,
27}
28
29impl<'a, Message, Theme, Renderer> Stack<'a, Message, Theme, Renderer>
30where
31    Renderer: crate::core::Renderer,
32{
33    /// Creates an empty [`Stack`].
34    pub fn new() -> Self {
35        Self::from_vec(Vec::new())
36    }
37
38    /// Creates a [`Stack`] with the given capacity.
39    pub fn with_capacity(capacity: usize) -> Self {
40        Self::from_vec(Vec::with_capacity(capacity))
41    }
42
43    /// Creates a [`Stack`] with the given elements.
44    pub fn with_children(
45        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
46    ) -> Self {
47        let iterator = children.into_iter();
48
49        Self::with_capacity(iterator.size_hint().0).extend(iterator)
50    }
51
52    /// Creates a [`Stack`] from an already allocated [`Vec`].
53    ///
54    /// Keep in mind that the [`Stack`] will not inspect the [`Vec`], which means
55    /// it won't automatically adapt to the sizing strategy of its contents.
56    ///
57    /// If any of the children have a [`Length::Fill`] strategy, you will need to
58    /// call [`Stack::width`] or [`Stack::height`] accordingly.
59    pub fn from_vec(
60        children: Vec<Element<'a, Message, Theme, Renderer>>,
61    ) -> Self {
62        Self {
63            width: Length::Shrink,
64            height: Length::Shrink,
65            children,
66            clip: false,
67        }
68    }
69
70    /// Sets the width of the [`Stack`].
71    pub fn width(mut self, width: impl Into<Length>) -> Self {
72        self.width = width.into();
73        self
74    }
75
76    /// Sets the height of the [`Stack`].
77    pub fn height(mut self, height: impl Into<Length>) -> Self {
78        self.height = height.into();
79        self
80    }
81
82    /// Adds an element to the [`Stack`].
83    pub fn push(
84        mut self,
85        child: impl Into<Element<'a, Message, Theme, Renderer>>,
86    ) -> Self {
87        let child = child.into();
88
89        if self.children.is_empty() {
90            let child_size = child.as_widget().size_hint();
91
92            self.width = self.width.enclose(child_size.width);
93            self.height = self.height.enclose(child_size.height);
94        }
95
96        self.children.push(child);
97        self
98    }
99
100    /// Adds an element to the [`Stack`], if `Some`.
101    pub fn push_maybe(
102        self,
103        child: Option<impl Into<Element<'a, Message, Theme, Renderer>>>,
104    ) -> Self {
105        if let Some(child) = child {
106            self.push(child)
107        } else {
108            self
109        }
110    }
111
112    /// Extends the [`Stack`] with the given children.
113    pub fn extend(
114        self,
115        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
116    ) -> Self {
117        children.into_iter().fold(self, Self::push)
118    }
119
120    /// Sets whether the [`Stack`] should clip overflowing content.
121    ///
122    /// It has a slight performance overhead during presentation.
123    ///
124    /// By default, it is set to `false`.
125    pub fn clip(mut self, clip: bool) -> Self {
126        self.clip = clip;
127        self
128    }
129}
130
131impl<Message, Renderer> Default for Stack<'_, Message, Renderer>
132where
133    Renderer: crate::core::Renderer,
134{
135    fn default() -> Self {
136        Self::new()
137    }
138}
139
140impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
141    for Stack<'a, Message, Theme, Renderer>
142where
143    Renderer: crate::core::Renderer,
144{
145    fn children(&self) -> Vec<Tree> {
146        self.children.iter().map(Tree::new).collect()
147    }
148
149    fn diff(&self, tree: &mut Tree) {
150        tree.diff_children(&self.children);
151    }
152
153    fn size(&self) -> Size<Length> {
154        Size {
155            width: self.width,
156            height: self.height,
157        }
158    }
159
160    fn layout(
161        &mut self,
162        tree: &mut Tree,
163        renderer: &Renderer,
164        limits: &layout::Limits,
165    ) -> layout::Node {
166        let limits = limits.width(self.width).height(self.height);
167
168        if self.children.is_empty() {
169            return layout::Node::new(limits.resolve(
170                self.width,
171                self.height,
172                Size::ZERO,
173            ));
174        }
175
176        let base = self.children[0].as_widget_mut().layout(
177            &mut tree.children[0],
178            renderer,
179            &limits,
180        );
181
182        let size = limits.resolve(self.width, self.height, base.size());
183        let limits = layout::Limits::new(Size::ZERO, size);
184
185        let nodes = std::iter::once(base)
186            .chain(
187                self.children[1..]
188                    .iter_mut()
189                    .zip(&mut tree.children[1..])
190                    .map(|(layer, tree)| {
191                        layer.as_widget_mut().layout(tree, renderer, &limits)
192                    }),
193            )
194            .collect();
195
196        layout::Node::with_children(size, nodes)
197    }
198
199    fn operate(
200        &mut self,
201        tree: &mut Tree,
202        layout: Layout<'_>,
203        renderer: &Renderer,
204        operation: &mut dyn Operation,
205    ) {
206        operation.container(None, layout.bounds(), &mut |operation| {
207            self.children
208                .iter_mut()
209                .zip(&mut tree.children)
210                .zip(layout.children())
211                .for_each(|((child, state), layout)| {
212                    child
213                        .as_widget_mut()
214                        .operate(state, layout, renderer, operation);
215                });
216        });
217    }
218
219    fn update(
220        &mut self,
221        tree: &mut Tree,
222        event: &Event,
223        layout: Layout<'_>,
224        mut cursor: mouse::Cursor,
225        renderer: &Renderer,
226        clipboard: &mut dyn Clipboard,
227        shell: &mut Shell<'_, Message>,
228        viewport: &Rectangle,
229    ) {
230        let is_over = cursor.is_over(layout.bounds());
231        let end = self.children.len() - 1;
232
233        for (i, ((child, state), layout)) in self
234            .children
235            .iter_mut()
236            .rev()
237            .zip(tree.children.iter_mut().rev())
238            .zip(layout.children().rev())
239            .enumerate()
240        {
241            child.as_widget_mut().update(
242                state, event, layout, cursor, renderer, clipboard, shell,
243                viewport,
244            );
245
246            if shell.is_event_captured() {
247                return;
248            }
249
250            if i < end && is_over && !cursor.is_levitating() {
251                let interaction = child.as_widget().mouse_interaction(
252                    state, layout, cursor, viewport, renderer,
253                );
254
255                if interaction != mouse::Interaction::None {
256                    cursor = cursor.levitate();
257                }
258            }
259        }
260    }
261
262    fn mouse_interaction(
263        &self,
264        tree: &Tree,
265        layout: Layout<'_>,
266        cursor: mouse::Cursor,
267        viewport: &Rectangle,
268        renderer: &Renderer,
269    ) -> mouse::Interaction {
270        self.children
271            .iter()
272            .rev()
273            .zip(tree.children.iter().rev())
274            .zip(layout.children().rev())
275            .map(|((child, state), layout)| {
276                child.as_widget().mouse_interaction(
277                    state, layout, cursor, viewport, renderer,
278                )
279            })
280            .find(|&interaction| interaction != mouse::Interaction::None)
281            .unwrap_or_default()
282    }
283
284    fn draw(
285        &self,
286        tree: &Tree,
287        renderer: &mut Renderer,
288        theme: &Theme,
289        style: &renderer::Style,
290        layout: Layout<'_>,
291        cursor: mouse::Cursor,
292        viewport: &Rectangle,
293    ) {
294        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
295            let viewport = if self.clip {
296                &clipped_viewport
297            } else {
298                viewport
299            };
300
301            let layers_below = if cursor.is_over(layout.bounds()) {
302                self.children
303                    .iter()
304                    .rev()
305                    .zip(tree.children.iter().rev())
306                    .zip(layout.children().rev())
307                    .position(|((layer, state), layout)| {
308                        let interaction = layer.as_widget().mouse_interaction(
309                            state, layout, cursor, viewport, renderer,
310                        );
311
312                        interaction != mouse::Interaction::None
313                    })
314                    .map(|i| self.children.len() - i - 1)
315                    .unwrap_or_default()
316            } else {
317                0
318            };
319
320            let mut layers = self
321                .children
322                .iter()
323                .zip(&tree.children)
324                .zip(layout.children())
325                .enumerate();
326
327            let layers = layers.by_ref();
328
329            let mut draw_layer =
330                |i,
331                 layer: &Element<'a, Message, Theme, Renderer>,
332                 state,
333                 layout,
334                 cursor| {
335                    if i > 0 {
336                        renderer.with_layer(*viewport, |renderer| {
337                            layer.as_widget().draw(
338                                state, renderer, theme, style, layout, cursor,
339                                viewport,
340                            );
341                        });
342                    } else {
343                        layer.as_widget().draw(
344                            state, renderer, theme, style, layout, cursor,
345                            viewport,
346                        );
347                    }
348                };
349
350            for (i, ((layer, state), layout)) in layers.take(layers_below) {
351                draw_layer(i, layer, state, layout, mouse::Cursor::Unavailable);
352            }
353
354            for (i, ((layer, state), layout)) in layers {
355                draw_layer(i, layer, state, layout, cursor);
356            }
357        }
358    }
359
360    fn overlay<'b>(
361        &'b mut self,
362        tree: &'b mut Tree,
363        layout: Layout<'b>,
364        renderer: &Renderer,
365        viewport: &Rectangle,
366        translation: Vector,
367    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
368        overlay::from_children(
369            &mut self.children,
370            tree,
371            layout,
372            renderer,
373            viewport,
374            translation,
375        )
376    }
377}
378
379impl<'a, Message, Theme, Renderer> From<Stack<'a, Message, Theme, Renderer>>
380    for Element<'a, Message, Theme, Renderer>
381where
382    Message: 'a,
383    Theme: 'a,
384    Renderer: crate::core::Renderer + 'a,
385{
386    fn from(stack: Stack<'a, Message, Theme, Renderer>) -> Self {
387        Self::new(stack)
388    }
389}