From b0205e03d8e4794850e55e8c4bf83a40dd41aa9d Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sun, 19 Feb 2023 17:43:13 -0800 Subject: Use nested for lazy widgets --- runtime/src/lib.rs | 1 + runtime/src/overlay.rs | 4 + runtime/src/overlay/nested.rs | 353 ++++++++++++++++++++++++++++++++++ runtime/src/user_interface.rs | 6 +- runtime/src/user_interface/overlay.rs | 342 -------------------------------- 5 files changed, 360 insertions(+), 346 deletions(-) create mode 100644 runtime/src/overlay.rs create mode 100644 runtime/src/overlay/nested.rs delete mode 100644 runtime/src/user_interface/overlay.rs (limited to 'runtime') diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 50abf7b2..4bbf9687 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -47,6 +47,7 @@ pub mod clipboard; pub mod command; pub mod font; pub mod keyboard; +pub mod overlay; pub mod program; pub mod system; pub mod user_interface; diff --git a/runtime/src/overlay.rs b/runtime/src/overlay.rs new file mode 100644 index 00000000..03390980 --- /dev/null +++ b/runtime/src/overlay.rs @@ -0,0 +1,4 @@ +//! Overlays for user interfaces. +mod nested; + +pub use nested::Nested; diff --git a/runtime/src/overlay/nested.rs b/runtime/src/overlay/nested.rs new file mode 100644 index 00000000..5c5fafde --- /dev/null +++ b/runtime/src/overlay/nested.rs @@ -0,0 +1,353 @@ +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, Point, Rectangle, Shell, Size}; + +/// An [`Overlay`] container that displays nested overlays +#[allow(missing_debug_implementations)] +pub struct Nested<'a, Message, Renderer> { + overlay: overlay::Element<'a, Message, Renderer>, +} + +impl<'a, Message, Renderer> Nested<'a, Message, Renderer> +where + Renderer: renderer::Renderer, +{ + /// Creates a nested overlay from the provided [`overlay::Element`] + pub fn new(element: overlay::Element<'a, Message, Renderer>) -> Self { + Self { overlay: element } + } + + /// Returns the position of the [`Nested`] overlay. + pub fn position(&self) -> Point { + self.overlay.position() + } + + /// Returns the layout [`Node`] of the [`Nested`] overlay. + pub fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node + where + Renderer: renderer::Renderer, + { + let translation = position - element.position(); + + let node = element.layout(renderer, bounds, translation); + + if let Some(mut nested) = + element.overlay(Layout::new(&node), renderer) + { + layout::Node::with_children( + node.size(), + vec![ + node, + recurse(&mut nested, renderer, bounds, position), + ], + ) + } else { + layout::Node::with_children(node.size(), vec![node]) + } + } + + recurse(&mut self.overlay, renderer, bounds, position) + } + + /// Draws the [`Nested`] overlay using the associated `Renderer`. + pub fn draw( + &mut self, + renderer: &mut Renderer, + theme: &::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + renderer: &mut Renderer, + theme: &::Theme, + style: &renderer::Style, + cursor: mouse::Cursor, + ) where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + let nested_layout = layouts.next(); + + let is_over = cursor + .position() + .zip(nested_layout) + .and_then(|(cursor_position, nested_layout)| { + element.overlay(layout, renderer).map(|nested| { + nested.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 nested, nested_layout)) = + element.overlay(layout, renderer).zip(nested_layout) + { + recurse( + &mut nested, + nested_layout, + renderer, + theme, + style, + cursor, + ); + } + } + } + + recurse(&mut self.overlay, layout, renderer, theme, style, cursor); + } + + /// Applies a [`widget::Operation`] to the [`Nested`] overlay. + pub fn operate( + &mut self, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + element.operate(layout, renderer, operation); + + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse(&mut nested, nested_layout, renderer, operation); + } + } + } + + recurse(&mut self.overlay, layout, renderer, operation) + } + + /// Processes a runtime [`Event`]. + pub 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( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + event: Event, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> (event::Status, bool) + where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + let (nested_status, nested_is_over) = + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse( + &mut nested, + nested_layout, + event.clone(), + cursor, + renderer, + clipboard, + shell, + ) + } else { + (event::Status::Ignored, false) + }; + + if matches!(nested_status, event::Status::Ignored) { + let is_over = nested_is_over + || cursor + .position() + .map(|cursor_position| { + element.is_over( + layout, + renderer, + cursor_position, + ) + }) + .unwrap_or_default(); + + ( + element.on_event( + event, + layout, + if nested_is_over { + mouse::Cursor::Unavailable + } else { + cursor + }, + renderer, + clipboard, + shell, + ), + is_over, + ) + } else { + (nested_status, nested_is_over) + } + } else { + (event::Status::Ignored, false) + } + } + + let (status, _) = recurse( + &mut self.overlay, + layout, + event, + cursor, + renderer, + clipboard, + shell, + ); + + status + } + + /// Returns the current [`mouse::Interaction`] of the [`Nested`] overlay. + pub fn mouse_interaction( + &mut self, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> Option + where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + let layout = layouts.next()?; + let cursor_position = cursor.position()?; + + if !element.is_over(layout, renderer, cursor_position) { + return None; + } + + Some( + element + .overlay(layout, renderer) + .zip(layouts.next()) + .and_then(|(mut overlay, layout)| { + recurse( + &mut overlay, + layout, + cursor, + viewport, + renderer, + ) + }) + .unwrap_or_else(|| { + element.mouse_interaction( + layout, cursor, viewport, renderer, + ) + }), + ) + } + + recurse(&mut self.overlay, layout, cursor, viewport, renderer) + .unwrap_or_default() + } + + /// Returns true if the cursor is over the [`Nested`] overlay. + pub fn is_over( + &mut self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool + where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + if element.is_over(layout, renderer, cursor_position) { + return true; + } + + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse( + &mut nested, + nested_layout, + renderer, + cursor_position, + ) + } else { + false + } + } else { + false + } + } + + recurse(&mut self.overlay, layout, renderer, cursor_position) + } +} diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 1d55970e..619423fd 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -1,14 +1,12 @@ //! Implement your own event loop to drive a user interface. -mod overlay; - use crate::core::event::{self, Event}; use crate::core::layout; use crate::core::mouse; use crate::core::renderer; use crate::core::widget; use crate::core::window; -use crate::core::{Clipboard, Point, Rectangle, Size}; -use crate::core::{Element, Layout, Shell}; +use crate::core::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; +use crate::overlay; /// A set of interactive graphical elements with a specific [`Layout`]. /// diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs deleted file mode 100644 index 1211d55b..00000000 --- a/runtime/src/user_interface/overlay.rs +++ /dev/null @@ -1,342 +0,0 @@ -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, Point, Rectangle, Shell, Size}; - -/// An [`Overlay`] container that displays nested overlays -#[allow(missing_debug_implementations)] -pub struct Nested<'a, Message, Renderer> { - overlay: overlay::Element<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Nested<'a, Message, Renderer> -where - Renderer: renderer::Renderer, -{ - /// Creates a nested overlay from the provided [`overlay::Element`] - pub fn new(element: overlay::Element<'a, Message, Renderer>) -> Self { - Self { overlay: element } - } - - pub fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - position: Point, - ) -> layout::Node { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - renderer: &Renderer, - bounds: Size, - position: Point, - ) -> layout::Node - where - Renderer: renderer::Renderer, - { - let translation = position - Point::ORIGIN; - - let node = element.layout(renderer, bounds, translation); - - if let Some(mut nested) = - element.overlay(Layout::new(&node), renderer) - { - layout::Node::with_children( - node.size(), - vec![ - node, - recurse(&mut nested, renderer, bounds, position), - ], - ) - } else { - layout::Node::with_children(node.size(), vec![node]) - } - } - - recurse(&mut self.overlay, renderer, bounds, position) - } - - pub fn draw( - &mut self, - renderer: &mut Renderer, - theme: &::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - renderer: &mut Renderer, - theme: &::Theme, - style: &renderer::Style, - cursor: mouse::Cursor, - ) where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - let nested_layout = layouts.next(); - - let is_over = cursor - .position() - .zip(nested_layout) - .and_then(|(cursor_position, nested_layout)| { - element.overlay(layout, renderer).map(|nested| { - nested.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 nested, nested_layout)) = - element.overlay(layout, renderer).zip(nested_layout) - { - recurse( - &mut nested, - nested_layout, - renderer, - theme, - style, - cursor, - ); - } - } - } - - recurse(&mut self.overlay, layout, renderer, theme, style, cursor); - } - - pub fn operate( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - element.operate(layout, renderer, operation); - - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse(&mut nested, nested_layout, renderer, operation); - } - } - } - - recurse(&mut self.overlay, layout, renderer, operation) - } - - pub 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( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - event: Event, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> (event::Status, bool) - where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - let (nested_status, nested_is_over) = - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse( - &mut nested, - nested_layout, - event.clone(), - cursor, - renderer, - clipboard, - shell, - ) - } else { - (event::Status::Ignored, false) - }; - - if matches!(nested_status, event::Status::Ignored) { - let is_over = nested_is_over - || cursor - .position() - .map(|cursor_position| { - element.is_over( - layout, - renderer, - cursor_position, - ) - }) - .unwrap_or_default(); - - ( - element.on_event( - event, - layout, - if nested_is_over { - mouse::Cursor::Unavailable - } else { - cursor - }, - renderer, - clipboard, - shell, - ), - is_over, - ) - } else { - (nested_status, nested_is_over) - } - } else { - (event::Status::Ignored, false) - } - } - - let (status, _) = recurse( - &mut self.overlay, - layout, - event, - cursor, - renderer, - clipboard, - shell, - ); - - status - } - - pub fn mouse_interaction( - &mut self, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - renderer: &Renderer, - ) -> Option - where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - let layout = layouts.next()?; - let cursor_position = cursor.position()?; - - if !element.is_over(layout, renderer, cursor_position) { - return None; - } - - Some( - element - .overlay(layout, renderer) - .zip(layouts.next()) - .and_then(|(mut overlay, layout)| { - recurse( - &mut overlay, - layout, - cursor, - viewport, - renderer, - ) - }) - .unwrap_or_else(|| { - element.mouse_interaction( - layout, cursor, viewport, renderer, - ) - }), - ) - } - - recurse(&mut self.overlay, layout, cursor, viewport, renderer) - .unwrap_or_default() - } - - pub fn is_over( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - cursor_position: Point, - ) -> bool { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - renderer: &Renderer, - cursor_position: Point, - ) -> bool - where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - if element.is_over(layout, renderer, cursor_position) { - return true; - } - - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse( - &mut nested, - nested_layout, - renderer, - cursor_position, - ) - } else { - false - } - } else { - false - } - } - - recurse(&mut self.overlay, layout, renderer, cursor_position) - } -} -- cgit