diff options
Diffstat (limited to '')
| -rw-r--r-- | runtime/src/user_interface.rs | 71 | ||||
| -rw-r--r-- | runtime/src/user_interface/overlay.rs | 288 | 
2 files changed, 332 insertions, 27 deletions
diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 68ff6158..8ae0363a 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -1,12 +1,14 @@  //! 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, Rectangle, Size, Vector}; -use crate::core::{Element, Layout, Shell}; +use crate::core::{Clipboard, Point, Rectangle, Size}; +use crate::core::{Element, Layout, Overlay, Shell};  /// A set of interactive graphical elements with a specific [`Layout`].  /// @@ -185,18 +187,18 @@ where          let mut outdated = false;          let mut redraw_request = None; -        let mut manual_overlay = -            ManuallyDrop::new(self.root.as_widget_mut().overlay( -                &mut self.state, -                Layout::new(&self.base), -                renderer, -            )); +        let mut manual_overlay = ManuallyDrop::new( +            self.root +                .as_widget_mut() +                .overlay(&mut self.state, Layout::new(&self.base), renderer) +                .map(overlay::Nested::new), +        );          let (base_cursor, overlay_statuses) = if manual_overlay.is_some() {              let bounds = self.bounds;              let mut overlay = manual_overlay.as_mut().unwrap(); -            let mut layout = overlay.layout(renderer, bounds, Vector::ZERO); +            let mut layout = overlay.layout(renderer, bounds, Point::ORIGIN);              let mut event_statuses = Vec::new();              for event in events.iter().cloned() { @@ -231,12 +233,16 @@ where                          &layout::Limits::new(Size::ZERO, self.bounds),                      ); -                    manual_overlay = -                        ManuallyDrop::new(self.root.as_widget_mut().overlay( -                            &mut self.state, -                            Layout::new(&self.base), -                            renderer, -                        )); +                    manual_overlay = ManuallyDrop::new( +                        self.root +                            .as_widget_mut() +                            .overlay( +                                &mut self.state, +                                Layout::new(&self.base), +                                renderer, +                            ) +                            .map(overlay::Nested::new), +                    );                      if manual_overlay.is_none() {                          break; @@ -245,7 +251,8 @@ where                      overlay = manual_overlay.as_mut().unwrap();                      shell.revalidate_layout(|| { -                        layout = overlay.layout(renderer, bounds, Vector::ZERO); +                        layout = +                            overlay.layout(renderer, bounds, Point::ORIGIN);                      });                  } @@ -260,8 +267,11 @@ where                      cursor                          .position()                          .map(|cursor_position| { -                            overlay -                                .is_over(Layout::new(&layout), cursor_position) +                            overlay.is_over( +                                Layout::new(&layout), +                                renderer, +                                cursor_position, +                            )                          })                          .unwrap_or_default()                  }) @@ -428,16 +438,20 @@ where              .root              .as_widget_mut()              .overlay(&mut self.state, Layout::new(&self.base), renderer) +            .map(overlay::Nested::new)          {              let overlay_layout = self.overlay.take().unwrap_or_else(|| { -                overlay.layout(renderer, self.bounds, Vector::ZERO) +                overlay.layout(renderer, self.bounds, Point::ORIGIN)              });              let cursor = if cursor                  .position()                  .map(|cursor_position| { -                    overlay -                        .is_over(Layout::new(&overlay_layout), cursor_position) +                    overlay.is_over( +                        Layout::new(&overlay_layout), +                        renderer, +                        cursor_position, +                    )                  })                  .unwrap_or_default()              { @@ -488,6 +502,7 @@ where              .and_then(|layout| {                  root.as_widget_mut()                      .overlay(&mut self.state, Layout::new(base), renderer) +                    .map(overlay::Nested::new)                      .map(|overlay| {                          let overlay_interaction = overlay.mouse_interaction(                              Layout::new(layout), @@ -513,6 +528,7 @@ where                              .map(|cursor_position| {                                  overlay.is_over(                                      Layout::new(layout), +                                    renderer,                                      cursor_position,                                  )                              }) @@ -540,14 +556,15 @@ where              operation,          ); -        if let Some(mut overlay) = self.root.as_widget_mut().overlay( -            &mut self.state, -            Layout::new(&self.base), -            renderer, -        ) { +        if let Some(mut overlay) = self +            .root +            .as_widget_mut() +            .overlay(&mut self.state, Layout::new(&self.base), renderer) +            .map(overlay::Nested::new) +        {              if self.overlay.is_none() {                  self.overlay = -                    Some(overlay.layout(renderer, self.bounds, Vector::ZERO)); +                    Some(overlay.layout(renderer, self.bounds, Point::ORIGIN));              }              overlay.operate( diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs new file mode 100644 index 00000000..6dfed153 --- /dev/null +++ b/runtime/src/user_interface/overlay.rs @@ -0,0 +1,288 @@ +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; + +/// 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<overlay::Element<'a, Message, Renderer>>, +); + +impl<'a, Message, Renderer> Inner<'a, Message, Renderer> { +    fn with_element_mut<T>( +        &self, +        mut f: impl FnMut(&mut overlay::Element<'_, Message, Renderer>) -> T, +    ) -> T { +        (f)(&mut self.0.borrow_mut()) +    } +} + +impl<'a, Message, Renderer> Overlay<Message, Renderer> +    for Nested<'a, Message, Renderer> +where +    Renderer: renderer::Renderer, +{ +    fn layout( +        &self, +        renderer: &Renderer, +        bounds: Size, +        position: Point, +    ) -> layout::Node { +        fn recurse<Message, Renderer>( +            element: &mut overlay::Element<'_, Message, Renderer>, +            renderer: &Renderer, +            bounds: Size, +            position: Point, +        ) -> Vec<layout::Node> +        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: &<Renderer as renderer::Renderer>::Theme, +        style: &renderer::Style, +        layout: Layout<'_>, +        cursor: mouse::Cursor, +    ) { +        fn recurse<'a, Message, Renderer>( +            element: &mut overlay::Element<'_, Message, Renderer>, +            mut layouts: impl Iterator<Item = Layout<'a>>, +            renderer: &mut Renderer, +            theme: &<Renderer as renderer::Renderer>::Theme, +            style: &renderer::Style, +            cursor: mouse::Cursor, +        ) where +            Renderer: renderer::Renderer, +        { +            let layout = layouts.next().unwrap(); + +            element.draw(renderer, theme, style, layout, 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(); + +            recurse(element, layouts, renderer, theme, style, cursor); +        }) +    } + +    fn operate( +        &mut self, +        layout: Layout<'_>, +        renderer: &Renderer, +        operation: &mut dyn widget::Operation<Message>, +    ) { +        fn recurse<'a, Message, Renderer>( +            element: &mut overlay::Element<'_, Message, Renderer>, +            mut layouts: impl Iterator<Item = Layout<'a>>, +            renderer: &Renderer, +            operation: &mut dyn widget::Operation<Message>, +        ) where +            Renderer: renderer::Renderer, +        { +            let layout = layouts.next().unwrap(); + +            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<Item = Layout<'a>>, +            event: Event, +            cursor: mouse::Cursor, +            renderer: &Renderer, +            clipboard: &mut dyn Clipboard, +            shell: &mut Shell<'_, Message>, +        ) -> event::Status +        where +            Renderer: renderer::Renderer, +        { +            let layout = layouts.next().unwrap(); + +            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 +            } +        } + +        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<Item = Layout<'a>>, +            cursor: mouse::Cursor, +            viewport: &Rectangle, +            renderer: &Renderer, +        ) -> mouse::Interaction +        where +            Renderer: renderer::Renderer, +        { +            let layout = layouts.next().unwrap(); + +            let interaction = +                if let Some(mut overlay) = element.overlay(layout, renderer) { +                    recurse(&mut overlay, layouts, cursor, viewport, renderer) +                } else { +                    mouse::Interaction::default() +                }; + +            element +                .mouse_interaction(layout, cursor, viewport, renderer) +                .max(interaction) +        } + +        self.overlay.with_element_mut(|element| { +            let layouts = layout.children(); + +            recurse(element, layouts, cursor, viewport, renderer) +        }) +    } + +    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<Item = Layout<'a>>, +            renderer: &Renderer, +            cursor_position: Point, +        ) -> bool +        where +            Renderer: renderer::Renderer, +        { +            let layout = layouts.next().unwrap(); + +            let is_over = element.is_over(layout, renderer, cursor_position); + +            if is_over { +                return true; +            } + +            if let Some(mut overlay) = element.overlay(layout, renderer) { +                recurse(&mut overlay, layouts, renderer, cursor_position) +            } 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<overlay::Element<'b, Message, Renderer>> { +        None +    } +}  | 
