iced_widget/
stack.rs

1//! Display content on top of other content.
2
3use crate::core::event::{self, Event};
4use crate::core::layout;
5use crate::core::mouse;
6use crate::core::overlay;
7use crate::core::renderer;
8use crate::core::widget::{Operation, Tree};
9use crate::core::{
10    Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
11};
12
13/// A container that displays children on top of each other.
14///
15/// The first [`Element`] dictates the intrinsic [`Size`] of a [`Stack`] and
16/// will be displayed as the base layer. Every consecutive [`Element`] will be
17/// renderer on top; on its own layer.
18///
19/// Keep in mind that too much layering will normally produce bad UX as well as
20/// introduce certain rendering overhead. Use this widget sparingly!
21#[allow(missing_debug_implementations)]
22pub struct Stack<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
23{
24    width: Length,
25    height: Length,
26    children: Vec<Element<'a, Message, Theme, Renderer>>,
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        }
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
88        if self.children.is_empty() {
89            let child_size = child.as_widget().size_hint();
90
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        self
97    }
98
99    /// Adds an element to the [`Stack`], if `Some`.
100    pub fn push_maybe(
101        self,
102        child: Option<impl Into<Element<'a, Message, Theme, Renderer>>>,
103    ) -> Self {
104        if let Some(child) = child {
105            self.push(child)
106        } else {
107            self
108        }
109    }
110
111    /// Extends the [`Stack`] with the given children.
112    pub fn extend(
113        self,
114        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
115    ) -> Self {
116        children.into_iter().fold(self, Self::push)
117    }
118}
119
120impl<'a, Message, Renderer> Default for Stack<'a, 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(&mut self, tree: &mut Tree) {
139        tree.diff_children(&mut 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        &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().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(self.children[1..].iter().zip(&mut tree.children[1..]).map(
176                |(layer, tree)| {
177                    let node =
178                        layer.as_widget().layout(tree, renderer, &limits);
179
180                    node
181                },
182            ))
183            .collect();
184
185        layout::Node::with_children(size, nodes)
186    }
187
188    fn operate(
189        &self,
190        tree: &mut Tree,
191        layout: Layout<'_>,
192        renderer: &Renderer,
193        operation: &mut dyn Operation,
194    ) {
195        operation.container(None, layout.bounds(), &mut |operation| {
196            self.children
197                .iter()
198                .zip(&mut tree.children)
199                .zip(layout.children())
200                .for_each(|((child, state), layout)| {
201                    child
202                        .as_widget()
203                        .operate(state, layout, renderer, operation);
204                });
205        });
206    }
207
208    fn on_event(
209        &mut self,
210        tree: &mut Tree,
211        event: Event,
212        layout: Layout<'_>,
213        mut cursor: mouse::Cursor,
214        renderer: &Renderer,
215        clipboard: &mut dyn Clipboard,
216        shell: &mut Shell<'_, Message>,
217        viewport: &Rectangle,
218    ) -> event::Status {
219        let is_over = cursor.is_over(layout.bounds());
220
221        self.children
222            .iter_mut()
223            .rev()
224            .zip(tree.children.iter_mut().rev())
225            .zip(layout.children().rev())
226            .map(|((child, state), layout)| {
227                let status = child.as_widget_mut().on_event(
228                    state,
229                    event.clone(),
230                    layout,
231                    cursor,
232                    renderer,
233                    clipboard,
234                    shell,
235                    viewport,
236                );
237
238                if is_over && cursor != mouse::Cursor::Unavailable {
239                    let interaction = child.as_widget().mouse_interaction(
240                        state, layout, cursor, viewport, renderer,
241                    );
242
243                    if interaction != mouse::Interaction::None {
244                        cursor = mouse::Cursor::Unavailable;
245                    }
246                }
247
248                status
249            })
250            .find(|&status| status == event::Status::Captured)
251            .unwrap_or(event::Status::Ignored)
252    }
253
254    fn mouse_interaction(
255        &self,
256        tree: &Tree,
257        layout: Layout<'_>,
258        cursor: mouse::Cursor,
259        viewport: &Rectangle,
260        renderer: &Renderer,
261    ) -> mouse::Interaction {
262        self.children
263            .iter()
264            .rev()
265            .zip(tree.children.iter().rev())
266            .zip(layout.children().rev())
267            .map(|((child, state), layout)| {
268                child.as_widget().mouse_interaction(
269                    state, layout, cursor, viewport, renderer,
270                )
271            })
272            .find(|&interaction| interaction != mouse::Interaction::None)
273            .unwrap_or_default()
274    }
275
276    fn draw(
277        &self,
278        tree: &Tree,
279        renderer: &mut Renderer,
280        theme: &Theme,
281        style: &renderer::Style,
282        layout: Layout<'_>,
283        cursor: mouse::Cursor,
284        viewport: &Rectangle,
285    ) {
286        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
287            let layers_below = if cursor.is_over(layout.bounds()) {
288                self.children
289                    .iter()
290                    .rev()
291                    .zip(tree.children.iter().rev())
292                    .zip(layout.children().rev())
293                    .position(|((layer, state), layout)| {
294                        let interaction = layer.as_widget().mouse_interaction(
295                            state, layout, cursor, viewport, renderer,
296                        );
297
298                        interaction != mouse::Interaction::None
299                    })
300                    .map(|i| self.children.len() - i - 1)
301                    .unwrap_or_default()
302            } else {
303                0
304            };
305
306            let mut layers = self
307                .children
308                .iter()
309                .zip(&tree.children)
310                .zip(layout.children())
311                .enumerate();
312
313            let layers = layers.by_ref();
314
315            let mut draw_layer =
316                |i,
317                 layer: &Element<'a, Message, Theme, Renderer>,
318                 state,
319                 layout,
320                 cursor| {
321                    if i > 0 {
322                        renderer.with_layer(clipped_viewport, |renderer| {
323                            layer.as_widget().draw(
324                                state,
325                                renderer,
326                                theme,
327                                style,
328                                layout,
329                                cursor,
330                                &clipped_viewport,
331                            );
332                        });
333                    } else {
334                        layer.as_widget().draw(
335                            state,
336                            renderer,
337                            theme,
338                            style,
339                            layout,
340                            cursor,
341                            &clipped_viewport,
342                        );
343                    }
344                };
345
346            for (i, ((layer, state), layout)) in layers.take(layers_below) {
347                draw_layer(i, layer, state, layout, mouse::Cursor::Unavailable);
348            }
349
350            for (i, ((layer, state), layout)) in layers {
351                draw_layer(i, layer, state, layout, cursor);
352            }
353        }
354    }
355
356    fn overlay<'b>(
357        &'b mut self,
358        tree: &'b mut Tree,
359        layout: Layout<'_>,
360        renderer: &Renderer,
361        translation: Vector,
362    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
363        overlay::from_children(
364            &mut self.children,
365            tree,
366            layout,
367            renderer,
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}