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