Introduce Layer trait

This commit is contained in:
Héctor Ramón Jiménez 2020-04-16 13:22:00 +02:00
parent c901f40fd6
commit f064f0482b
12 changed files with 252 additions and 54 deletions

View File

@ -1,7 +1,9 @@
use crate::{Backend, Defaults, Primitive};
use iced_native::layout::{self, Layout};
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.
#[derive(Debug)]
@ -53,6 +55,31 @@ where
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>

View File

@ -274,8 +274,9 @@ where
pub fn overlay(
&mut self,
) -> Option<Box<dyn Overlay<Message, Renderer> + 'a>> {
self.widget.overlay()
layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> {
self.widget.overlay(layout)
}
}

34
native/src/layer.rs Normal file
View 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>,
) {
}
}

View File

@ -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`].
///
/// The returned [`Rectangle`] describes the position and size of a

View File

@ -48,6 +48,7 @@ mod clipboard;
mod element;
mod event;
mod hasher;
mod layer;
mod overlay;
mod runtime;
mod user_interface;
@ -75,6 +76,7 @@ pub use debug::Debug;
pub use element::Element;
pub use event::Event;
pub use hasher::Hasher;
pub use layer::Layer;
pub use layout::Layout;
pub use overlay::Overlay;
pub use program::Program;

View File

@ -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
Renderer: crate::Renderer,
{
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node;
pub fn new(
position: Point,
layer: Box<dyn Layer<Message, Renderer> + 'a>,
) -> Self {
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,
renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>,
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,
_event: Event,
_layout: Layout<'_>,

View File

@ -25,7 +25,7 @@ mod null;
#[cfg(debug_assertions)]
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
/// output for its users.
@ -56,4 +56,11 @@ pub trait Renderer: Sized {
) -> layout::Node {
element.layout(self, limits)
}
fn overlay(
&mut self,
base: Self::Output,
overlay: Self::Output,
overlay_bounds: Rectangle,
) -> Self::Output;
}

View File

@ -22,6 +22,9 @@ impl Null {
impl Renderer for Null {
type Output = ();
type Defaults = ();
fn overlay(&mut self, _base: (), _overlay: (), _overlay_bounds: Rectangle) {
}
}
impl column::Renderer for Null {

View File

@ -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;
@ -19,12 +19,17 @@ use std::hash::Hasher;
/// [`UserInterface`]: struct.UserInterface.html
#[allow(missing_debug_implementations)]
pub struct UserInterface<'a, Message, Renderer> {
hash: u64,
root: Element<'a, Message, Renderer>,
layout: layout::Node,
base: Layer<Element<'a, Message, Renderer>>,
overlay: Option<Layer<Overlay<'a, Message, Renderer>>>,
bounds: Size,
}
struct Layer<T> {
root: T,
layout: layout::Node,
hash: u64,
}
impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer>
where
Renderer: crate::Renderer,
@ -92,27 +97,45 @@ where
cache: Cache,
renderer: &mut Renderer,
) -> Self {
let root = root.into();
let mut root = root.into();
let hash = {
let hasher = &mut crate::Hasher::default();
root.hash_layout(hasher);
let (base, overlay) = {
let hash = {
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 {
cache.layout
} else {
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds))
};
hasher.finish()
};
let layout = root.layout(&renderer, bounds);
Layer { root, layout, hash }
});
UserInterface {
hash,
root,
layout,
base,
overlay,
bounds,
}
}
@ -193,14 +216,42 @@ where
let mut messages = Vec::new();
for event in events {
self.root.widget.on_event(
event,
Layout::new(&self.layout),
cursor_position,
&mut messages,
renderer,
clipboard,
);
if let Some(overlay) = &mut self.overlay {
overlay.root.on_event(
event.clone(),
Layout::new(&overlay.layout),
cursor_position,
&mut messages,
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
@ -280,12 +331,42 @@ where
renderer: &mut Renderer,
cursor_position: Point,
) -> Renderer::Output {
self.root.widget.draw(
renderer,
&Renderer::Defaults::default(),
Layout::new(&self.layout),
cursor_position,
)
if let Some(overlay) = &self.overlay {
let overlay_bounds = overlay.layout.bounds();
let base_cursor = if overlay_bounds.contains(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
@ -295,8 +376,8 @@ where
/// [`UserInterface`]: struct.UserInterface.html
pub fn into_cache(self) -> Cache {
Cache {
hash: self.hash,
layout: self.layout,
hash: self.base.hash,
layout: self.base.layout,
bounds: self.bounds,
}
}

View File

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

View File

@ -2,8 +2,8 @@
use std::hash::Hash;
use crate::{
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point,
Widget,
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Overlay,
Point, Widget,
};
use std::u32;
@ -204,6 +204,17 @@ where
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`].

View File

@ -2,8 +2,8 @@
use std::hash::Hash;
use crate::{
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point,
Rectangle, Widget,
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Overlay,
Point, Rectangle, Widget,
};
use std::u32;
@ -214,6 +214,13 @@ where
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`].