summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/src/lib.rs1
-rw-r--r--runtime/src/overlay.rs4
-rw-r--r--runtime/src/overlay/nested.rs353
-rw-r--r--runtime/src/program/state.rs10
-rw-r--r--runtime/src/user_interface.rs145
5 files changed, 453 insertions, 60 deletions
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..b729f769
--- /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<Message, Renderer>(
+ 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)
+ }
+
+ /// Draws the [`Nested`] overlay using the associated `Renderer`.
+ pub fn draw(
+ &mut self,
+ renderer: &mut Renderer,
+ theme: &<Renderer as renderer::Renderer>::Theme,
+ style: &renderer::Style,
+ layout: Layout<'_>,
+ cursor: mouse::Cursor,
+ ) {
+ fn recurse<Message, Renderer>(
+ element: &mut overlay::Element<'_, Message, Renderer>,
+ layout: Layout<'_>,
+ renderer: &mut Renderer,
+ theme: &<Renderer as renderer::Renderer>::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.children().next().unwrap(),
+ 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<Message>,
+ ) {
+ fn recurse<Message, Renderer>(
+ element: &mut overlay::Element<'_, Message, Renderer>,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ operation: &mut dyn widget::Operation<Message>,
+ ) 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<Message, Renderer>(
+ 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<Message, Renderer>(
+ element: &mut overlay::Element<'_, Message, Renderer>,
+ layout: Layout<'_>,
+ cursor: mouse::Cursor,
+ viewport: &Rectangle,
+ renderer: &Renderer,
+ ) -> Option<mouse::Interaction>
+ 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<Message, Renderer>(
+ 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/program/state.rs b/runtime/src/program/state.rs
index 2fa9934d..d83e3f54 100644
--- a/runtime/src/program/state.rs
+++ b/runtime/src/program/state.rs
@@ -1,7 +1,7 @@
use crate::core::event::{self, Event};
use crate::core::mouse;
use crate::core::renderer;
-use crate::core::{Clipboard, Point, Size};
+use crate::core::{Clipboard, Size};
use crate::user_interface::{self, UserInterface};
use crate::{Command, Debug, Program};
@@ -88,7 +88,7 @@ where
pub fn update(
&mut self,
bounds: Size,
- cursor_position: Point,
+ cursor: mouse::Cursor,
renderer: &mut P::Renderer,
theme: &<P::Renderer as iced_core::Renderer>::Theme,
style: &renderer::Style,
@@ -108,7 +108,7 @@ where
let (_, event_statuses) = user_interface.update(
&self.queued_events,
- cursor_position,
+ cursor,
renderer,
clipboard,
&mut messages,
@@ -131,7 +131,7 @@ where
let command = if messages.is_empty() {
debug.draw_started();
self.mouse_interaction =
- user_interface.draw(renderer, theme, style, cursor_position);
+ user_interface.draw(renderer, theme, style, cursor);
debug.draw_finished();
self.cache = Some(user_interface.into_cache());
@@ -163,7 +163,7 @@ where
debug.draw_started();
self.mouse_interaction =
- user_interface.draw(renderer, theme, style, cursor_position);
+ user_interface.draw(renderer, theme, style, cursor);
debug.draw_finished();
self.cache = Some(user_interface.into_cache());
diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs
index d9206134..619423fd 100644
--- a/runtime/src/user_interface.rs
+++ b/runtime/src/user_interface.rs
@@ -5,8 +5,8 @@ use crate::core::mouse;
use crate::core::renderer;
use crate::core::widget;
use crate::core::window;
-use crate::core::{Clipboard, Point, Rectangle, Size, Vector};
-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`].
///
@@ -128,7 +128,9 @@ where
/// # pub fn view(&self) -> iced_core::Element<(), Renderer> { unimplemented!() }
/// # pub fn update(&mut self, _: ()) {}
/// # }
- /// use iced_runtime::core::{clipboard, Size, Point};
+ /// use iced_runtime::core::clipboard;
+ /// use iced_runtime::core::mouse;
+ /// use iced_runtime::core::Size;
/// use iced_runtime::user_interface::{self, UserInterface};
/// use iced_wgpu::Renderer;
///
@@ -136,7 +138,7 @@ where
/// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0);
- /// let mut cursor_position = Point::default();
+ /// let mut cursor = mouse::Cursor::default();
/// let mut clipboard = clipboard::Null;
///
/// // Initialize our event storage
@@ -156,7 +158,7 @@ where
/// // Update the user interface
/// let (state, event_statuses) = user_interface.update(
/// &events,
- /// cursor_position,
+ /// cursor,
/// &mut renderer,
/// &mut clipboard,
/// &mut messages
@@ -173,7 +175,7 @@ where
pub fn update(
&mut self,
events: &[Event],
- cursor_position: Point,
+ cursor: mouse::Cursor,
renderer: &mut Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
@@ -183,18 +185,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() {
@@ -203,7 +205,7 @@ where
let event_status = overlay.on_event(
event,
Layout::new(&layout),
- cursor_position,
+ cursor,
renderer,
clipboard,
&mut shell,
@@ -229,12 +231,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;
@@ -243,7 +249,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);
});
}
@@ -252,22 +259,29 @@ where
}
}
- let base_cursor = manual_overlay
- .as_ref()
- .filter(|overlay| {
- overlay.is_over(Layout::new(&layout), cursor_position)
- })
- .map(|_| {
- // TODO: Type-safe cursor availability
- Point::new(-1.0, -1.0)
+ let base_cursor = if manual_overlay
+ .as_mut()
+ .and_then(|overlay| {
+ cursor.position().map(|cursor_position| {
+ overlay.is_over(
+ Layout::new(&layout),
+ renderer,
+ cursor_position,
+ )
+ })
})
- .unwrap_or(cursor_position);
+ .unwrap_or_default()
+ {
+ mouse::Cursor::Unavailable
+ } else {
+ cursor
+ };
self.overlay = Some(layout);
(base_cursor, event_statuses)
} else {
- (cursor_position, vec![event::Status::Ignored; events.len()])
+ (cursor, vec![event::Status::Ignored; events.len()])
};
let _ = ManuallyDrop::into_inner(manual_overlay);
@@ -359,8 +373,9 @@ where
/// # pub fn update(&mut self, _: ()) {}
/// # }
/// use iced_runtime::core::clipboard;
+ /// use iced_runtime::core::mouse;
/// use iced_runtime::core::renderer;
- /// use iced_runtime::core::{Element, Size, Point};
+ /// use iced_runtime::core::{Element, Size};
/// use iced_runtime::user_interface::{self, UserInterface};
/// use iced_wgpu::{Renderer, Theme};
///
@@ -368,7 +383,7 @@ where
/// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0);
- /// let mut cursor_position = Point::default();
+ /// let mut cursor = mouse::Cursor::default();
/// let mut clipboard = clipboard::Null;
/// let mut events = Vec::new();
/// let mut messages = Vec::new();
@@ -387,14 +402,14 @@ where
/// // Update the user interface
/// let event_statuses = user_interface.update(
/// &events,
- /// cursor_position,
+ /// cursor,
/// &mut renderer,
/// &mut clipboard,
/// &mut messages
/// );
///
/// // Draw the user interface
- /// let mouse_cursor = user_interface.draw(&mut renderer, &theme, &renderer::Style::default(), cursor_position);
+ /// let mouse_interaction = user_interface.draw(&mut renderer, &theme, &renderer::Style::default(), cursor);
///
/// cache = user_interface.into_cache();
///
@@ -411,35 +426,44 @@ where
renderer: &mut Renderer,
theme: &Renderer::Theme,
style: &renderer::Style,
- cursor_position: Point,
+ cursor: mouse::Cursor,
) -> mouse::Interaction {
// TODO: Move to shell level (?)
renderer.clear();
let viewport = Rectangle::with_size(self.bounds);
- let base_cursor = if let Some(overlay) = self
+ let base_cursor = if let Some(mut overlay) = self
.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 new_cursor_position = if overlay
- .is_over(Layout::new(&overlay_layout), cursor_position)
+ let cursor = if cursor
+ .position()
+ .map(|cursor_position| {
+ overlay.is_over(
+ Layout::new(&overlay_layout),
+ renderer,
+ cursor_position,
+ )
+ })
+ .unwrap_or_default()
{
- Point::new(-1.0, -1.0)
+ mouse::Cursor::Unavailable
} else {
- cursor_position
+ cursor
};
self.overlay = Some(overlay_layout);
- new_cursor_position
+ cursor
} else {
- cursor_position
+ cursor
};
self.root.as_widget().draw(
@@ -455,7 +479,7 @@ where
let base_interaction = self.root.as_widget().mouse_interaction(
&self.state,
Layout::new(&self.base),
- cursor_position,
+ base_cursor,
&viewport,
renderer,
);
@@ -477,10 +501,11 @@ where
.and_then(|layout| {
root.as_widget_mut()
.overlay(&mut self.state, Layout::new(base), renderer)
- .map(|overlay| {
+ .map(overlay::Nested::new)
+ .map(|mut overlay| {
let overlay_interaction = overlay.mouse_interaction(
Layout::new(layout),
- cursor_position,
+ cursor,
&viewport,
renderer,
);
@@ -493,11 +518,20 @@ where
theme,
style,
Layout::new(layout),
- cursor_position,
+ cursor,
);
});
- if overlay.is_over(Layout::new(layout), cursor_position)
+ if cursor
+ .position()
+ .map(|cursor_position| {
+ overlay.is_over(
+ Layout::new(layout),
+ renderer,
+ cursor_position,
+ )
+ })
+ .unwrap_or_default()
{
overlay_interaction
} else {
@@ -521,14 +555,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(