//! Display content on top of other content. use crate::core::layout; use crate::core::mouse; use crate::core::overlay; use crate::core::renderer; use crate::core::widget::{Operation, Tree}; use crate::core::{ Clipboard, Element, Event, Layout, Length, Rectangle, Shell, Size, Vector, Widget, }; /// A container that displays children on top of each other. /// /// The first [`Element`] dictates the intrinsic [`Size`] of a [`Stack`] and /// will be displayed as the base layer. Every consecutive [`Element`] will be /// renderer on top; on its own layer. /// /// Keep in mind that too much layering will normally produce bad UX as well as /// introduce certain rendering overhead. Use this widget sparingly! #[allow(missing_debug_implementations)] pub struct Stack<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> { width: Length, height: Length, children: Vec>, } impl<'a, Message, Theme, Renderer> Stack<'a, Message, Theme, Renderer> where Renderer: crate::core::Renderer, { /// Creates an empty [`Stack`]. pub fn new() -> Self { Self::from_vec(Vec::new()) } /// Creates a [`Stack`] with the given capacity. pub fn with_capacity(capacity: usize) -> Self { Self::from_vec(Vec::with_capacity(capacity)) } /// Creates a [`Stack`] with the given elements. pub fn with_children( children: impl IntoIterator>, ) -> Self { let iterator = children.into_iter(); Self::with_capacity(iterator.size_hint().0).extend(iterator) } /// Creates a [`Stack`] from an already allocated [`Vec`]. /// /// Keep in mind that the [`Stack`] will not inspect the [`Vec`], which means /// it won't automatically adapt to the sizing strategy of its contents. /// /// If any of the children have a [`Length::Fill`] strategy, you will need to /// call [`Stack::width`] or [`Stack::height`] accordingly. pub fn from_vec( children: Vec>, ) -> Self { Self { width: Length::Shrink, height: Length::Shrink, children, } } /// Sets the width of the [`Stack`]. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); self } /// Sets the height of the [`Stack`]. pub fn height(mut self, height: impl Into) -> Self { self.height = height.into(); self } /// Adds an element to the [`Stack`]. pub fn push( mut self, child: impl Into>, ) -> Self { let child = child.into(); if self.children.is_empty() { let child_size = child.as_widget().size_hint(); self.width = self.width.enclose(child_size.width); self.height = self.height.enclose(child_size.height); } self.children.push(child); self } /// Adds an element to the [`Stack`], if `Some`. pub fn push_maybe( self, child: Option>>, ) -> Self { if let Some(child) = child { self.push(child) } else { self } } /// Extends the [`Stack`] with the given children. pub fn extend( self, children: impl IntoIterator>, ) -> Self { children.into_iter().fold(self, Self::push) } } impl Default for Stack<'_, Message, Renderer> where Renderer: crate::core::Renderer, { fn default() -> Self { Self::new() } } impl<'a, Message, Theme, Renderer> Widget for Stack<'a, Message, Theme, Renderer> where Renderer: crate::core::Renderer, { fn children(&self) -> Vec { self.children.iter().map(Tree::new).collect() } fn diff(&self, tree: &mut Tree) { tree.diff_children(&self.children); } fn size(&self) -> Size { Size { width: self.width, height: self.height, } } fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let limits = limits.width(self.width).height(self.height); if self.children.is_empty() { return layout::Node::new(limits.resolve( self.width, self.height, Size::ZERO, )); } let base = self.children[0].as_widget().layout( &mut tree.children[0], renderer, &limits, ); let size = limits.resolve(self.width, self.height, base.size()); let limits = layout::Limits::new(Size::ZERO, size); let nodes = std::iter::once(base) .chain(self.children[1..].iter().zip(&mut tree.children[1..]).map( |(layer, tree)| { let node = layer.as_widget().layout(tree, renderer, &limits); node }, )) .collect(); layout::Node::with_children(size, nodes) } fn operate( &self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { operation.container(None, layout.bounds(), &mut |operation| { self.children .iter() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child .as_widget() .operate(state, layout, renderer, operation); }); }); } fn update( &mut self, tree: &mut Tree, event: &Event, layout: Layout<'_>, mut cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { let is_over = cursor.is_over(layout.bounds()); let end = self.children.len() - 1; for (i, ((child, state), layout)) in self .children .iter_mut() .rev() .zip(tree.children.iter_mut().rev()) .zip(layout.children().rev()) .enumerate() { child.as_widget_mut().update( state, event, layout, cursor, renderer, clipboard, shell, viewport, ); if shell.is_event_captured() { return; } if i < end && is_over && !cursor.is_levitating() { let interaction = child.as_widget().mouse_interaction( state, layout, cursor, viewport, renderer, ); if interaction != mouse::Interaction::None { cursor = cursor.levitate(); } } } } fn mouse_interaction( &self, tree: &Tree, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.children .iter() .rev() .zip(tree.children.iter().rev()) .zip(layout.children().rev()) .map(|((child, state), layout)| { child.as_widget().mouse_interaction( state, layout, cursor, viewport, renderer, ) }) .find(|&interaction| interaction != mouse::Interaction::None) .unwrap_or_default() } fn draw( &self, tree: &Tree, renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, ) { if let Some(clipped_viewport) = layout.bounds().intersection(viewport) { let layers_below = if cursor.is_over(layout.bounds()) { self.children .iter() .rev() .zip(tree.children.iter().rev()) .zip(layout.children().rev()) .position(|((layer, state), layout)| { let interaction = layer.as_widget().mouse_interaction( state, layout, cursor, viewport, renderer, ); interaction != mouse::Interaction::None }) .map(|i| self.children.len() - i - 1) .unwrap_or_default() } else { 0 }; let mut layers = self .children .iter() .zip(&tree.children) .zip(layout.children()) .enumerate(); let layers = layers.by_ref(); let mut draw_layer = |i, layer: &Element<'a, Message, Theme, Renderer>, state, layout, cursor| { if i > 0 { renderer.with_layer(clipped_viewport, |renderer| { layer.as_widget().draw( state, renderer, theme, style, layout, cursor, &clipped_viewport, ); }); } else { layer.as_widget().draw( state, renderer, theme, style, layout, cursor, &clipped_viewport, ); } }; for (i, ((layer, state), layout)) in layers.take(layers_below) { draw_layer(i, layer, state, layout, mouse::Cursor::Unavailable); } for (i, ((layer, state), layout)) in layers { draw_layer(i, layer, state, layout, cursor); } } } fn overlay<'b>( &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, translation: Vector, ) -> Option> { overlay::from_children( &mut self.children, tree, layout, renderer, translation, ) } } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, Theme: 'a, Renderer: crate::core::Renderer + 'a, { fn from(stack: Stack<'a, Message, Theme, Renderer>) -> Self { Self::new(stack) } }