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