Introduce Layer
trait
This commit is contained in:
parent
c901f40fd6
commit
f064f0482b
@ -1,7 +1,9 @@
|
|||||||
use crate::{Backend, Defaults, Primitive};
|
use crate::{Backend, Defaults, Primitive};
|
||||||
use iced_native::layout::{self, Layout};
|
use iced_native::layout::{self, Layout};
|
||||||
use iced_native::mouse;
|
use iced_native::mouse;
|
||||||
use iced_native::{Background, Color, Element, Point, Widget};
|
use iced_native::{
|
||||||
|
Background, Color, Element, Point, Rectangle, Vector, Widget,
|
||||||
|
};
|
||||||
|
|
||||||
/// A backend-agnostic renderer that supports all the built-in widgets.
|
/// A backend-agnostic renderer that supports all the built-in widgets.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -53,6 +55,31 @@ where
|
|||||||
|
|
||||||
layout
|
layout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(
|
||||||
|
&mut self,
|
||||||
|
(base_primitive, base_cursor): (Primitive, mouse::Interaction),
|
||||||
|
(overlay_primitives, overlay_cursor): (Primitive, mouse::Interaction),
|
||||||
|
overlay_bounds: Rectangle,
|
||||||
|
) -> (Primitive, mouse::Interaction) {
|
||||||
|
(
|
||||||
|
Primitive::Group {
|
||||||
|
primitives: vec![
|
||||||
|
base_primitive,
|
||||||
|
Primitive::Clip {
|
||||||
|
bounds: overlay_bounds,
|
||||||
|
offset: Vector::new(0, 0),
|
||||||
|
content: Box::new(overlay_primitives),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
if base_cursor > overlay_cursor {
|
||||||
|
base_cursor
|
||||||
|
} else {
|
||||||
|
overlay_cursor
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> layout::Debugger for Renderer<B>
|
impl<B> layout::Debugger for Renderer<B>
|
||||||
|
@ -274,8 +274,9 @@ where
|
|||||||
|
|
||||||
pub fn overlay(
|
pub fn overlay(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Option<Box<dyn Overlay<Message, Renderer> + 'a>> {
|
layout: Layout<'_>,
|
||||||
self.widget.overlay()
|
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||||
|
self.widget.overlay(layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
34
native/src/layer.rs
Normal file
34
native/src/layer.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use crate::{layout, Clipboard, Event, Hasher, Layout, Point, Size};
|
||||||
|
|
||||||
|
pub trait Layer<Message, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: crate::Renderer,
|
||||||
|
{
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
bounds: Size,
|
||||||
|
position: Point,
|
||||||
|
) -> layout::Node;
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
defaults: &Renderer::Defaults,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: Point,
|
||||||
|
) -> Renderer::Output;
|
||||||
|
|
||||||
|
fn hash_layout(&self, state: &mut Hasher, position: Point);
|
||||||
|
|
||||||
|
fn on_event(
|
||||||
|
&mut self,
|
||||||
|
_event: Event,
|
||||||
|
_layout: Layout<'_>,
|
||||||
|
_cursor_position: Point,
|
||||||
|
_messages: &mut Vec<Message>,
|
||||||
|
_renderer: &Renderer,
|
||||||
|
_clipboard: Option<&dyn Clipboard>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,13 @@ impl<'a> Layout<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the position of the [`Layout`].
|
||||||
|
///
|
||||||
|
/// [`Layout`]: struct.Layout.html
|
||||||
|
pub fn position(&self) -> Point {
|
||||||
|
self.position
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the bounds of the [`Layout`].
|
/// Gets the bounds of the [`Layout`].
|
||||||
///
|
///
|
||||||
/// The returned [`Rectangle`] describes the position and size of a
|
/// The returned [`Rectangle`] describes the position and size of a
|
||||||
|
@ -48,6 +48,7 @@ mod clipboard;
|
|||||||
mod element;
|
mod element;
|
||||||
mod event;
|
mod event;
|
||||||
mod hasher;
|
mod hasher;
|
||||||
|
mod layer;
|
||||||
mod overlay;
|
mod overlay;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
mod user_interface;
|
mod user_interface;
|
||||||
@ -75,6 +76,7 @@ pub use debug::Debug;
|
|||||||
pub use element::Element;
|
pub use element::Element;
|
||||||
pub use event::Event;
|
pub use event::Event;
|
||||||
pub use hasher::Hasher;
|
pub use hasher::Hasher;
|
||||||
|
pub use layer::Layer;
|
||||||
pub use layout::Layout;
|
pub use layout::Layout;
|
||||||
pub use overlay::Overlay;
|
pub use overlay::Overlay;
|
||||||
pub use program::Program;
|
pub use program::Program;
|
||||||
|
@ -1,26 +1,41 @@
|
|||||||
use crate::{layout, Clipboard, Event, Hasher, Layout, Point};
|
use crate::{layout, Clipboard, Event, Hasher, Layer, Layout, Point, Size};
|
||||||
|
|
||||||
pub trait Overlay<Message, Renderer>
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct Overlay<'a, Message, Renderer> {
|
||||||
|
position: Point,
|
||||||
|
layer: Box<dyn Layer<Message, Renderer> + 'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer> Overlay<'a, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: crate::Renderer,
|
Renderer: crate::Renderer,
|
||||||
{
|
{
|
||||||
fn layout(
|
pub fn new(
|
||||||
&self,
|
position: Point,
|
||||||
renderer: &Renderer,
|
layer: Box<dyn Layer<Message, Renderer> + 'a>,
|
||||||
limits: &layout::Limits,
|
) -> Self {
|
||||||
) -> layout::Node;
|
Self { position, layer }
|
||||||
|
}
|
||||||
|
|
||||||
fn draw(
|
pub fn layout(&self, renderer: &Renderer, bounds: Size) -> layout::Node {
|
||||||
|
self.layer.layout(renderer, bounds, self.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
defaults: &Renderer::Defaults,
|
defaults: &Renderer::Defaults,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
) -> Renderer::Output;
|
) -> Renderer::Output {
|
||||||
|
self.layer.draw(renderer, defaults, layout, cursor_position)
|
||||||
|
}
|
||||||
|
|
||||||
fn hash_layout(&self, state: &mut Hasher);
|
pub fn hash_layout(&self, state: &mut Hasher) {
|
||||||
|
self.layer.hash_layout(state, self.position);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_event(
|
pub fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
_event: Event,
|
_event: Event,
|
||||||
_layout: Layout<'_>,
|
_layout: Layout<'_>,
|
||||||
|
@ -25,7 +25,7 @@ mod null;
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub use null::Null;
|
pub use null::Null;
|
||||||
|
|
||||||
use crate::{layout, Element};
|
use crate::{layout, Element, Rectangle};
|
||||||
|
|
||||||
/// A component that can take the state of a user interface and produce an
|
/// A component that can take the state of a user interface and produce an
|
||||||
/// output for its users.
|
/// output for its users.
|
||||||
@ -56,4 +56,11 @@ pub trait Renderer: Sized {
|
|||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
element.layout(self, limits)
|
element.layout(self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(
|
||||||
|
&mut self,
|
||||||
|
base: Self::Output,
|
||||||
|
overlay: Self::Output,
|
||||||
|
overlay_bounds: Rectangle,
|
||||||
|
) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ impl Null {
|
|||||||
impl Renderer for Null {
|
impl Renderer for Null {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
type Defaults = ();
|
type Defaults = ();
|
||||||
|
|
||||||
|
fn overlay(&mut self, _base: (), _overlay: (), _overlay_bounds: Rectangle) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl column::Renderer for Null {
|
impl column::Renderer for Null {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{layout, Clipboard, Element, Event, Layout, Point, Size};
|
use crate::{layout, Clipboard, Element, Event, Layout, Overlay, Point, Size};
|
||||||
|
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
|
|
||||||
@ -19,12 +19,17 @@ use std::hash::Hasher;
|
|||||||
/// [`UserInterface`]: struct.UserInterface.html
|
/// [`UserInterface`]: struct.UserInterface.html
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
pub struct UserInterface<'a, Message, Renderer> {
|
pub struct UserInterface<'a, Message, Renderer> {
|
||||||
hash: u64,
|
base: Layer<Element<'a, Message, Renderer>>,
|
||||||
root: Element<'a, Message, Renderer>,
|
overlay: Option<Layer<Overlay<'a, Message, Renderer>>>,
|
||||||
layout: layout::Node,
|
|
||||||
bounds: Size,
|
bounds: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Layer<T> {
|
||||||
|
root: T,
|
||||||
|
layout: layout::Node,
|
||||||
|
hash: u64,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer>
|
impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: crate::Renderer,
|
Renderer: crate::Renderer,
|
||||||
@ -92,27 +97,45 @@ where
|
|||||||
cache: Cache,
|
cache: Cache,
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let root = root.into();
|
let mut root = root.into();
|
||||||
|
|
||||||
let hash = {
|
let (base, overlay) = {
|
||||||
let hasher = &mut crate::Hasher::default();
|
let hash = {
|
||||||
root.hash_layout(hasher);
|
let hasher = &mut crate::Hasher::default();
|
||||||
|
root.hash_layout(hasher);
|
||||||
|
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout_is_cached = hash == cache.hash && bounds == cache.bounds;
|
||||||
|
|
||||||
|
let layout = if layout_is_cached {
|
||||||
|
cache.layout
|
||||||
|
} else {
|
||||||
|
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds))
|
||||||
|
};
|
||||||
|
|
||||||
|
let overlay = root.overlay(Layout::new(&layout));
|
||||||
|
|
||||||
|
(Layer { root, layout, hash }, overlay)
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout_is_cached = hash == cache.hash && bounds == cache.bounds;
|
let overlay = overlay.map(|root| {
|
||||||
|
let hash = {
|
||||||
|
let hasher = &mut crate::Hasher::default();
|
||||||
|
root.hash_layout(hasher);
|
||||||
|
|
||||||
let layout = if layout_is_cached {
|
hasher.finish()
|
||||||
cache.layout
|
};
|
||||||
} else {
|
|
||||||
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds))
|
let layout = root.layout(&renderer, bounds);
|
||||||
};
|
|
||||||
|
Layer { root, layout, hash }
|
||||||
|
});
|
||||||
|
|
||||||
UserInterface {
|
UserInterface {
|
||||||
hash,
|
base,
|
||||||
root,
|
overlay,
|
||||||
layout,
|
|
||||||
bounds,
|
bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,14 +216,42 @@ where
|
|||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
|
|
||||||
for event in events {
|
for event in events {
|
||||||
self.root.widget.on_event(
|
if let Some(overlay) = &mut self.overlay {
|
||||||
event,
|
overlay.root.on_event(
|
||||||
Layout::new(&self.layout),
|
event.clone(),
|
||||||
cursor_position,
|
Layout::new(&overlay.layout),
|
||||||
&mut messages,
|
cursor_position,
|
||||||
renderer,
|
&mut messages,
|
||||||
clipboard,
|
renderer,
|
||||||
);
|
clipboard,
|
||||||
|
);
|
||||||
|
|
||||||
|
let base_cursor =
|
||||||
|
if overlay.layout.bounds().contains(cursor_position) {
|
||||||
|
// TODO: Encode cursor availability
|
||||||
|
Point::new(-1.0, -1.0)
|
||||||
|
} else {
|
||||||
|
cursor_position
|
||||||
|
};
|
||||||
|
|
||||||
|
self.base.root.widget.on_event(
|
||||||
|
event,
|
||||||
|
Layout::new(&self.base.layout),
|
||||||
|
base_cursor,
|
||||||
|
&mut messages,
|
||||||
|
renderer,
|
||||||
|
clipboard,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.base.root.widget.on_event(
|
||||||
|
event,
|
||||||
|
Layout::new(&self.base.layout),
|
||||||
|
cursor_position,
|
||||||
|
&mut messages,
|
||||||
|
renderer,
|
||||||
|
clipboard,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messages
|
messages
|
||||||
@ -280,12 +331,42 @@ where
|
|||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
) -> Renderer::Output {
|
) -> Renderer::Output {
|
||||||
self.root.widget.draw(
|
if let Some(overlay) = &self.overlay {
|
||||||
renderer,
|
let overlay_bounds = overlay.layout.bounds();
|
||||||
&Renderer::Defaults::default(),
|
|
||||||
Layout::new(&self.layout),
|
let base_cursor = if overlay_bounds.contains(cursor_position) {
|
||||||
cursor_position,
|
Point::new(-1.0, -1.0)
|
||||||
)
|
} else {
|
||||||
|
cursor_position
|
||||||
|
};
|
||||||
|
|
||||||
|
let base_primitives = self.base.root.widget.draw(
|
||||||
|
renderer,
|
||||||
|
&Renderer::Defaults::default(),
|
||||||
|
Layout::new(&self.base.layout),
|
||||||
|
base_cursor,
|
||||||
|
);
|
||||||
|
|
||||||
|
let overlay_primitives = overlay.root.draw(
|
||||||
|
renderer,
|
||||||
|
&Renderer::Defaults::default(),
|
||||||
|
Layout::new(&overlay.layout),
|
||||||
|
cursor_position,
|
||||||
|
);
|
||||||
|
|
||||||
|
renderer.overlay(
|
||||||
|
base_primitives,
|
||||||
|
overlay_primitives,
|
||||||
|
overlay_bounds,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.base.root.widget.draw(
|
||||||
|
renderer,
|
||||||
|
&Renderer::Defaults::default(),
|
||||||
|
Layout::new(&self.base.layout),
|
||||||
|
cursor_position,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the [`Cache`] of the [`UserInterface`], consuming it in the
|
/// Extract the [`Cache`] of the [`UserInterface`], consuming it in the
|
||||||
@ -295,8 +376,8 @@ where
|
|||||||
/// [`UserInterface`]: struct.UserInterface.html
|
/// [`UserInterface`]: struct.UserInterface.html
|
||||||
pub fn into_cache(self) -> Cache {
|
pub fn into_cache(self) -> Cache {
|
||||||
Cache {
|
Cache {
|
||||||
hash: self.hash,
|
hash: self.base.hash,
|
||||||
layout: self.layout,
|
layout: self.base.layout,
|
||||||
bounds: self.bounds,
|
bounds: self.bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,10 @@ where
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn overlay(&mut self) -> Option<Box<dyn Overlay<Message, Renderer> + 'a>> {
|
fn overlay(
|
||||||
|
&mut self,
|
||||||
|
_layout: Layout<'_>,
|
||||||
|
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point,
|
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Overlay,
|
||||||
Widget,
|
Point, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::u32;
|
use std::u32;
|
||||||
@ -204,6 +204,17 @@ where
|
|||||||
child.widget.hash_layout(state);
|
child.widget.hash_layout(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(
|
||||||
|
&mut self,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||||
|
self.children
|
||||||
|
.iter_mut()
|
||||||
|
.zip(layout.children())
|
||||||
|
.filter_map(|(child, layout)| child.widget.overlay(layout))
|
||||||
|
.next()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The renderer of a [`Column`].
|
/// The renderer of a [`Column`].
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point,
|
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Overlay,
|
||||||
Rectangle, Widget,
|
Point, Rectangle, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::u32;
|
use std::u32;
|
||||||
@ -214,6 +214,13 @@ where
|
|||||||
|
|
||||||
self.content.hash_layout(state);
|
self.content.hash_layout(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(
|
||||||
|
&mut self,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||||
|
self.content.overlay(layout.children().next().unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The renderer of a [`Container`].
|
/// The renderer of a [`Container`].
|
||||||
|
Loading…
Reference in New Issue
Block a user