use crate::core::event; use crate::core::layout; use crate::core::mouse; use crate::core::overlay; use crate::core::renderer; use crate::core::widget; use crate::core::{ Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size, }; use std::cell::RefCell; use std::iter::Peekable; /// An [`Overlay`] container that displays nested overlays #[allow(missing_debug_implementations)] pub struct Nested<'a, Message, Renderer> { overlay: Inner<'a, Message, Renderer>, } impl<'a, Message, Renderer> Nested<'a, Message, Renderer> { /// Creates a nested overlay from the provided [`overlay::Element`] pub fn new(element: overlay::Element<'a, Message, Renderer>) -> Self { Self { overlay: Inner(RefCell::new(element)), } } } struct Inner<'a, Message, Renderer>( RefCell>, ); impl<'a, Message, Renderer> Inner<'a, Message, Renderer> { fn with_element_mut( &self, mut f: impl FnMut(&mut overlay::Element<'_, Message, Renderer>) -> T, ) -> T { (f)(&mut self.0.borrow_mut()) } } impl<'a, Message, Renderer> Overlay for Nested<'a, Message, Renderer> where Renderer: renderer::Renderer, { fn layout( &self, renderer: &Renderer, bounds: Size, position: Point, ) -> layout::Node { fn recurse( element: &mut overlay::Element<'_, Message, Renderer>, renderer: &Renderer, bounds: Size, position: Point, ) -> Vec where Renderer: renderer::Renderer, { let translation = position - Point::ORIGIN; let node = element.layout(renderer, bounds, translation); if let Some(mut overlay) = element.overlay(Layout::new(&node), renderer) { vec![node] .into_iter() .chain(recurse(&mut overlay, renderer, bounds, position)) .collect() } else { vec![node] } } self.overlay.with_element_mut(|element| { layout::Node::with_children( bounds, recurse(element, renderer, bounds, position), ) }) } fn draw( &self, renderer: &mut Renderer, theme: &::Theme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, ) { fn recurse<'a, Message, Renderer>( element: &mut overlay::Element<'_, Message, Renderer>, mut layouts: Peekable>>, renderer: &mut Renderer, theme: &::Theme, style: &renderer::Style, cursor: mouse::Cursor, ) where Renderer: renderer::Renderer, { if let Some(layout) = layouts.next() { let is_over = cursor .position() .and_then(|cursor_position| { layouts.peek().and_then(|nested_layout| { element.overlay(layout, renderer).map(|overlay| { overlay.is_over( *nested_layout, renderer, cursor_position, ) }) }) }) .unwrap_or_default(); renderer.with_layer(layout.bounds(), |renderer| { element.draw( renderer, theme, style, layout, if is_over { mouse::Cursor::Unavailable } else { cursor }, ); }); if let Some(mut overlay) = element.overlay(layout, renderer) { recurse( &mut overlay, layouts, renderer, theme, style, cursor, ); } } } self.overlay.with_element_mut(|element| { let layouts = layout.children().peekable(); recurse(element, layouts, renderer, theme, style, cursor); }) } fn operate( &mut self, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { fn recurse<'a, Message, Renderer>( element: &mut overlay::Element<'_, Message, Renderer>, mut layouts: impl Iterator>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) where Renderer: renderer::Renderer, { if let Some(layout) = layouts.next() { element.operate(layout, renderer, operation); if let Some(mut overlay) = element.overlay(layout, renderer) { recurse(&mut overlay, layouts, renderer, operation); } } } let layouts = layout.children(); recurse(self.overlay.0.get_mut(), layouts, renderer, operation) } fn on_event( &mut self, event: Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { fn recurse<'a, Message, Renderer>( element: &mut overlay::Element<'_, Message, Renderer>, mut layouts: impl Iterator>, event: Event, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status where Renderer: renderer::Renderer, { if let Some(layout) = layouts.next() { let status = if let Some(mut overlay) = element.overlay(layout, renderer) { recurse( &mut overlay, layouts, event.clone(), cursor, renderer, clipboard, shell, ) } else { event::Status::Ignored }; if matches!(status, event::Status::Ignored) { element.on_event( event, layout, cursor, renderer, clipboard, shell, ) } else { status } } else { event::Status::Ignored } } let layouts = layout.children(); recurse( self.overlay.0.get_mut(), layouts, event, cursor, renderer, clipboard, shell, ) } fn mouse_interaction( &self, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { fn recurse<'a, Message, Renderer>( element: &mut overlay::Element<'_, Message, Renderer>, mut layouts: impl Iterator>, cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> Option where Renderer: renderer::Renderer, { let layout = layouts.next()?; let cursor_position = cursor.position()?; if !element.is_over(layout, renderer, cursor_position) { return None; } Some( element .overlay(layout, renderer) .and_then(|mut overlay| { recurse( &mut overlay, layouts, cursor, viewport, renderer, ) }) .unwrap_or_else(|| { element.mouse_interaction( layout, cursor, viewport, renderer, ) }), ) } self.overlay .with_element_mut(|element| { let layouts = layout.children(); recurse(element, layouts, cursor, viewport, renderer) }) .unwrap_or_default() } fn is_over( &self, layout: Layout<'_>, renderer: &Renderer, cursor_position: Point, ) -> bool { fn recurse<'a, Message, Renderer>( element: &mut overlay::Element<'_, Message, Renderer>, mut layouts: impl Iterator>, renderer: &Renderer, cursor_position: Point, ) -> bool where Renderer: renderer::Renderer, { if let Some(layout) = layouts.next() { if element.is_over(layout, renderer, cursor_position) { return true; } if let Some(mut overlay) = element.overlay(layout, renderer) { recurse(&mut overlay, layouts, renderer, cursor_position) } else { false } } else { false } } self.overlay.with_element_mut(|element| { let layouts = layout.children(); recurse(element, layouts, renderer, cursor_position) }) } fn overlay<'b>( &'b mut self, _layout: Layout<'_>, _renderer: &Renderer, ) -> Option> { None } }