Draft Widget::overlay idempotency

This commit is contained in:
Héctor Ramón Jiménez 2020-07-05 05:44:10 +02:00
parent 61f22b1db2
commit 625979b665
11 changed files with 134 additions and 131 deletions

View File

@ -1,4 +1,4 @@
use crate::backend::Backend; use crate::backend::{self, Backend};
use crate::{Primitive, Renderer}; use crate::{Primitive, Renderer};
use iced_native::{ use iced_native::{
mouse, overlay, Color, Font, HorizontalAlignment, Point, Rectangle, mouse, overlay, Color, Font, HorizontalAlignment, Point, Rectangle,
@ -9,7 +9,7 @@ pub use iced_style::menu::Style;
impl<B> overlay::menu::Renderer for Renderer<B> impl<B> overlay::menu::Renderer for Renderer<B>
where where
B: Backend, B: Backend + backend::Text,
{ {
type Style = Style; type Style = Style;

View File

@ -273,10 +273,10 @@ where
self.widget.hash_layout(state); self.widget.hash_layout(state);
} }
pub fn overlay( pub fn overlay<'b>(
&mut self, &'b mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'b, Message, Renderer>> {
self.widget.overlay(layout) self.widget.overlay(layout)
} }
} }
@ -366,10 +366,12 @@ where
fn overlay( fn overlay(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, B, Renderer>> { ) -> Option<Overlay<'_, B, Renderer>> {
let mapper = self.mapper.clone();
self.widget self.widget
.overlay(layout) .overlay(layout)
.map(|overlay| overlay.map(self.mapper.clone())) .map(move |overlay| overlay.map(mapper))
} }
} }
@ -450,7 +452,7 @@ where
fn overlay( fn overlay(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'_, Message, Renderer>> {
self.element.overlay(layout) self.element.overlay(layout)
} }
} }

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
container, layout, mouse, overlay, scrollable, Clipboard, Container, container, layout, mouse, overlay, scrollable, text, Clipboard, Container,
Element, Event, Hasher, Layout, Length, Point, Rectangle, Scrollable, Size, Element, Event, Hasher, Layout, Length, Point, Rectangle, Scrollable, Size,
Vector, Widget, Vector, Widget,
}; };
@ -39,10 +39,10 @@ where
pub fn new<T: 'a>( pub fn new<T: 'a>(
state: &'a mut State, state: &'a mut State,
options: impl Into<Cow<'a, [T]>>, options: impl Into<Cow<'a, [T]>>,
on_selected: Box<dyn Fn(T) -> Message>, on_selected: &'a dyn Fn(T) -> Message,
width: u16, width: u16,
target_height: f32, target_height: f32,
text_size: u16, text_size: Option<u16>,
padding: u16, padding: u16,
style: <Renderer as self::Renderer>::Style, style: <Renderer as self::Renderer>::Style,
) -> Self ) -> Self
@ -175,8 +175,8 @@ where
{ {
hovered_option: &'a mut Option<usize>, hovered_option: &'a mut Option<usize>,
options: Cow<'a, [T]>, options: Cow<'a, [T]>,
on_selected: Box<dyn Fn(T) -> Message>, on_selected: &'a dyn Fn(T) -> Message,
text_size: u16, text_size: Option<u16>,
padding: u16, padding: u16,
style: <Renderer as self::Renderer>::Style, style: <Renderer as self::Renderer>::Style,
} }
@ -188,8 +188,8 @@ where
pub fn new( pub fn new(
hovered_option: &'a mut Option<usize>, hovered_option: &'a mut Option<usize>,
options: impl Into<Cow<'a, [T]>>, options: impl Into<Cow<'a, [T]>>,
on_selected: Box<dyn Fn(T) -> Message>, on_selected: &'a dyn Fn(T) -> Message,
text_size: u16, text_size: Option<u16>,
padding: u16, padding: u16,
style: <Renderer as self::Renderer>::Style, style: <Renderer as self::Renderer>::Style,
) -> Self { ) -> Self {
@ -221,17 +221,18 @@ where
fn layout( fn layout(
&self, &self,
_renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
use std::f32; use std::f32;
let limits = limits.width(Length::Fill).height(Length::Shrink); let limits = limits.width(Length::Fill).height(Length::Shrink);
let text_size = self.text_size.unwrap_or(renderer.default_size());
let size = { let size = {
let intrinsic = Size::new( let intrinsic = Size::new(
0.0, 0.0,
f32::from(self.text_size + self.padding * 2) f32::from(text_size + self.padding * 2)
* self.options.len() as f32, * self.options.len() as f32,
); );
@ -253,7 +254,7 @@ where
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
messages: &mut Vec<Message>, messages: &mut Vec<Message>,
_renderer: &Renderer, renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>, _clipboard: Option<&dyn Clipboard>,
) { ) {
match event { match event {
@ -270,11 +271,13 @@ where
} }
Event::Mouse(mouse::Event::CursorMoved { .. }) => { Event::Mouse(mouse::Event::CursorMoved { .. }) => {
let bounds = layout.bounds(); let bounds = layout.bounds();
let text_size =
self.text_size.unwrap_or(renderer.default_size());
if bounds.contains(cursor_position) { if bounds.contains(cursor_position) {
*self.hovered_option = Some( *self.hovered_option = Some(
((cursor_position.y - bounds.y) ((cursor_position.y - bounds.y)
/ f32::from(self.text_size + self.padding * 2)) / f32::from(text_size + self.padding * 2))
as usize, as usize,
); );
} }
@ -296,14 +299,16 @@ where
cursor_position, cursor_position,
&self.options, &self.options,
*self.hovered_option, *self.hovered_option,
self.text_size, self.text_size.unwrap_or(renderer.default_size()),
self.padding, self.padding,
&self.style, &self.style,
) )
} }
} }
pub trait Renderer: scrollable::Renderer + container::Renderer { pub trait Renderer:
scrollable::Renderer + container::Renderer + text::Renderer
{
type Style: Default + Clone; type Style: Default + Clone;
fn decorate( fn decorate(

View File

@ -35,7 +35,7 @@ where
renderer: &mut P::Renderer, renderer: &mut P::Renderer,
debug: &mut Debug, debug: &mut Debug,
) -> Self { ) -> Self {
let user_interface = build_user_interface( let mut user_interface = build_user_interface(
&mut program, &mut program,
Cache::default(), Cache::default(),
renderer, renderer,
@ -153,7 +153,7 @@ where
command command
})); }));
let user_interface = build_user_interface( let mut user_interface = build_user_interface(
&mut self.program, &mut self.program,
temp_cache, temp_cache,
renderer, renderer,

View File

@ -1,4 +1,4 @@
use crate::{layout, Clipboard, Element, Event, Layout, Overlay, Point, Size}; use crate::{layout, Clipboard, Element, Event, Layout, Point, Size};
use std::hash::Hasher; use std::hash::Hasher;
@ -19,13 +19,13 @@ 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> {
base: Layer<Element<'a, Message, Renderer>>, root: Element<'a, Message, Renderer>,
overlay: Option<Layer<Overlay<'a, Message, Renderer>>>, base: Layer,
overlay: Option<Layer>,
bounds: Size, bounds: Size,
} }
struct Layer<T> { struct Layer {
root: T,
layout: layout::Node, layout: layout::Node,
hash: u64, hash: u64,
} }
@ -97,9 +97,9 @@ where
cache: Cache, cache: Cache,
renderer: &mut Renderer, renderer: &mut Renderer,
) -> Self { ) -> Self {
let mut root = root.into(); let root = root.into();
let (base, overlay) = { let base = {
let hash = { let hash = {
let hasher = &mut crate::Hasher::default(); let hasher = &mut crate::Hasher::default();
root.hash_layout(hasher); root.hash_layout(hasher);
@ -115,27 +115,13 @@ where
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds)) renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds))
}; };
let overlay = root.overlay(Layout::new(&layout)); Layer { layout, hash }
(Layer { root, layout, hash }, overlay)
}; };
let overlay = overlay.map(|root| {
let hash = {
let hasher = &mut crate::Hasher::default();
root.hash_layout(hasher);
hasher.finish()
};
let layout = root.layout(&renderer, bounds);
Layer { root, layout, hash }
});
UserInterface { UserInterface {
root,
base, base,
overlay, overlay: None,
bounds, bounds,
} }
} }
@ -215,35 +201,49 @@ where
) -> Vec<Message> { ) -> Vec<Message> {
let mut messages = Vec::new(); let mut messages = Vec::new();
for event in events { let base_events = if let Some(mut overlay) =
if let Some(overlay) = &mut self.overlay { self.root.overlay(Layout::new(&self.base.layout))
let base_cursor = {
if overlay.layout.bounds().contains(cursor_position) { let layer = {
// TODO: Encode cursor availability let new_hash = {
Point::new(-1.0, -1.0) let hasher = &mut crate::Hasher::default();
} else { overlay.hash_layout(hasher);
cursor_position
hasher.finish()
}; };
overlay.root.on_event( let layout = match self.overlay.take() {
Some(Layer { hash, layout }) if new_hash == hash => layout,
_ => overlay.layout(&renderer, self.bounds),
};
Layer {
layout,
hash: new_hash,
}
};
for event in events {
overlay.on_event(
event.clone(), event.clone(),
Layout::new(&overlay.layout), Layout::new(&layer.layout),
cursor_position, cursor_position,
&mut messages, &mut messages,
renderer, renderer,
clipboard, clipboard,
); );
}
self.base.root.widget.on_event( self.overlay = Some(layer);
event,
Layout::new(&self.base.layout), None
base_cursor,
&mut messages,
renderer,
clipboard,
);
} else { } else {
self.base.root.widget.on_event( Some(events)
};
if let Some(events) = base_events {
for event in events {
self.root.widget.on_event(
event, event,
Layout::new(&self.base.layout), Layout::new(&self.base.layout),
cursor_position, cursor_position,
@ -327,12 +327,12 @@ where
/// } /// }
/// ``` /// ```
pub fn draw( pub fn draw(
&self, &mut self,
renderer: &mut Renderer, renderer: &mut Renderer,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
if let Some(overlay) = &self.overlay { if let Some(layer) = &self.overlay {
let overlay_bounds = overlay.layout.bounds(); let overlay_bounds = layer.layout.bounds();
let base_cursor = if overlay_bounds.contains(cursor_position) { let base_cursor = if overlay_bounds.contains(cursor_position) {
Point::new(-1.0, -1.0) Point::new(-1.0, -1.0)
@ -340,17 +340,20 @@ where
cursor_position cursor_position
}; };
let base_primitives = self.base.root.widget.draw( let base_primitives = self.root.widget.draw(
renderer, renderer,
&Renderer::Defaults::default(), &Renderer::Defaults::default(),
Layout::new(&self.base.layout), Layout::new(&self.base.layout),
base_cursor, base_cursor,
); );
let overlay_primitives = overlay.root.draw( if let Some(overlay) =
self.root.overlay(Layout::new(&self.base.layout))
{
let overlay_primitives = overlay.draw(
renderer, renderer,
&Renderer::Defaults::default(), &Renderer::Defaults::default(),
Layout::new(&overlay.layout), Layout::new(&layer.layout),
cursor_position, cursor_position,
); );
@ -360,7 +363,10 @@ where
overlay_bounds, overlay_bounds,
) )
} else { } else {
self.base.root.widget.draw( base_primitives
}
} else {
self.root.widget.draw(
renderer, renderer,
&Renderer::Defaults::default(), &Renderer::Defaults::default(),
Layout::new(&self.base.layout), Layout::new(&self.base.layout),

View File

@ -179,10 +179,10 @@ where
) { ) {
} }
fn overlay( fn overlay<'b>(
&mut self, &'b mut self,
_layout: Layout<'_>, _layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'b, Message, Renderer>> {
None None
} }
} }

View File

@ -208,7 +208,7 @@ where
fn overlay( fn overlay(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'_, Message, Renderer>> {
self.children self.children
.iter_mut() .iter_mut()
.zip(layout.children()) .zip(layout.children())

View File

@ -10,7 +10,7 @@ pub struct ComboBox<'a, T, Message, Renderer: self::Renderer>
where where
[T]: ToOwned<Owned = Vec<T>>, [T]: ToOwned<Owned = Vec<T>>,
{ {
internal: Option<Internal<'a, T, Message>>, internal: Internal<'a, T, Message>,
options: Cow<'a, [T]>, options: Cow<'a, [T]>,
selected: Option<T>, selected: Option<T>,
width: Length, width: Length,
@ -42,10 +42,10 @@ where
on_selected: impl Fn(T) -> Message + 'static, on_selected: impl Fn(T) -> Message + 'static,
) -> Self { ) -> Self {
Self { Self {
internal: Some(Internal { internal: Internal {
menu: &mut state.menu, menu: &mut state.menu,
on_selected: Box::new(on_selected), on_selected: Box::new(on_selected),
}), },
options: options.into(), options: options.into(),
selected, selected,
width: Length::Shrink, width: Length::Shrink,
@ -180,18 +180,16 @@ where
) { ) {
match event { match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if let Some(internal) = &mut self.internal {
if layout.bounds().contains(cursor_position) { if layout.bounds().contains(cursor_position) {
let selected = self.selected.as_ref(); let selected = self.selected.as_ref();
internal.menu.open( self.internal.menu.open(
self.options self.options
.iter() .iter()
.position(|option| Some(option) == selected), .position(|option| Some(option) == selected),
); );
} }
} }
}
_ => {} _ => {}
} }
} }
@ -217,26 +215,19 @@ where
fn overlay( fn overlay(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'_, Message, Renderer>> {
let is_open = self if self.internal.menu.is_open() {
.internal
.as_ref()
.map(|internal| internal.menu.is_open())
.unwrap_or(false);
if is_open {
if let Some(Internal { menu, on_selected }) = self.internal.take() {
let bounds = layout.bounds(); let bounds = layout.bounds();
Some(Overlay::new( Some(Overlay::new(
layout.position(), layout.position(),
Box::new(Menu::new( Box::new(Menu::new(
menu, self.internal.menu,
self.options.clone(), self.options.clone(),
on_selected, &self.internal.on_selected,
bounds.width.round() as u16, bounds.width.round() as u16,
bounds.height, bounds.height,
self.text_size.unwrap_or(20), self.text_size,
self.padding, self.padding,
Renderer::menu_style(&self.style), Renderer::menu_style(&self.style),
)), )),
@ -244,9 +235,6 @@ where
} else { } else {
None None
} }
} else {
None
}
} }
} }

View File

@ -218,7 +218,7 @@ where
fn overlay( fn overlay(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'_, Message, Renderer>> {
self.content.overlay(layout.children().next().unwrap()) self.content.overlay(layout.children().next().unwrap())
} }
} }

View File

@ -210,7 +210,7 @@ where
fn overlay( fn overlay(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'_, Message, Renderer>> {
self.children self.children
.iter_mut() .iter_mut()
.zip(layout.children()) .zip(layout.children())

View File

@ -319,14 +319,16 @@ where
fn overlay( fn overlay(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
) -> Option<Overlay<'a, Message, Renderer>> { ) -> Option<Overlay<'_, Message, Renderer>> {
self.content let Self { content, state, .. } = self;
content
.overlay(layout.children().next().unwrap()) .overlay(layout.children().next().unwrap())
.map(|overlay| { .map(|overlay| {
let bounds = layout.bounds(); let bounds = layout.bounds();
let content_layout = layout.children().next().unwrap(); let content_layout = layout.children().next().unwrap();
let content_bounds = content_layout.bounds(); let content_bounds = content_layout.bounds();
let offset = self.state.offset(bounds, content_bounds); let offset = state.offset(bounds, content_bounds);
overlay.translate(Vector::new(0.0, -(offset as f32))) overlay.translate(Vector::new(0.0, -(offset as f32)))
}) })