Introduce internal overlay::Nested for UserInterface

This commit is contained in:
Cory Forsstrom 2023-02-18 14:31:38 -08:00 committed by Héctor Ramón Jiménez
parent 329fbc7b21
commit 55dc3b5619
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
9 changed files with 406 additions and 41 deletions

View file

@ -91,9 +91,23 @@ where
///
/// By default, it returns true if the bounds of the `layout` contain
/// the `cursor_position`.
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
fn is_over(
&self,
layout: Layout<'_>,
_renderer: &Renderer,
cursor_position: Point,
) -> bool {
layout.bounds().contains(cursor_position)
}
/// Returns the nested overlay of the [`Overlay`], if there is any.
fn overlay<'a>(
&'a mut self,
_layout: Layout<'_>,
_renderer: &Renderer,
) -> Option<Element<'a, Message, Renderer>> {
None
}
}
/// Returns a [`Group`] of overlay [`Element`] children.

View file

@ -112,8 +112,22 @@ where
}
/// Returns true if the cursor is over the [`Element`].
pub fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
self.overlay.is_over(layout, cursor_position)
pub fn is_over(
&self,
layout: Layout<'_>,
renderer: &Renderer,
cursor_position: Point,
) -> bool {
self.overlay.is_over(layout, renderer, cursor_position)
}
/// Returns the nested overlay of the [`Element`], if there is any.
pub fn overlay<'b>(
&'b mut self,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<Element<'b, Message, Renderer>> {
self.overlay.overlay(layout, renderer)
}
}
@ -248,7 +262,12 @@ where
self.content.draw(renderer, theme, style, layout, cursor)
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
self.content.is_over(layout, cursor_position)
fn is_over(
&self,
layout: Layout<'_>,
renderer: &Renderer,
cursor_position: Point,
) -> bool {
self.content.is_over(layout, renderer, cursor_position)
}
}

View file

@ -147,11 +147,18 @@ where
});
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
fn is_over(
&self,
layout: Layout<'_>,
renderer: &Renderer,
cursor_position: Point,
) -> bool {
self.children
.iter()
.zip(layout.children())
.any(|(child, layout)| child.is_over(layout, cursor_position))
.any(|(child, layout)| {
child.is_over(layout, renderer, cursor_position)
})
}
}

View file

@ -650,7 +650,12 @@ mod toast {
.unwrap_or_default()
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
fn is_over(
&self,
layout: Layout<'_>,
_renderer: &Renderer,
cursor_position: Point,
) -> bool {
layout
.children()
.any(|layout| layout.bounds().contains(cursor_position))

View file

@ -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(

View file

@ -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
}
}

View file

@ -377,9 +377,14 @@ where
.unwrap_or(event::Status::Ignored)
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
fn is_over(
&self,
layout: Layout<'_>,
renderer: &Renderer,
cursor_position: Point,
) -> bool {
self.with_overlay_maybe(|overlay| {
overlay.is_over(layout, cursor_position)
overlay.is_over(layout, renderer, cursor_position)
})
.unwrap_or_default()
}

View file

@ -655,9 +655,14 @@ where
event_status
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
fn is_over(
&self,
layout: Layout<'_>,
renderer: &Renderer,
cursor_position: Point,
) -> bool {
self.with_overlay_maybe(|overlay| {
overlay.is_over(layout, cursor_position)
overlay.is_over(layout, renderer, cursor_position)
})
.unwrap_or_default()
}

View file

@ -409,9 +409,14 @@ where
.unwrap_or(event::Status::Ignored)
}
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
fn is_over(
&self,
layout: Layout<'_>,
renderer: &Renderer,
cursor_position: Point,
) -> bool {
self.with_overlay_maybe(|overlay| {
overlay.is_over(layout, cursor_position)
overlay.is_over(layout, renderer, cursor_position)
})
.unwrap_or_default()
}