Draft Widget::overlay
idempotency
This commit is contained in:
parent
61f22b1db2
commit
625979b665
@ -1,4 +1,4 @@
|
||||
use crate::backend::Backend;
|
||||
use crate::backend::{self, Backend};
|
||||
use crate::{Primitive, Renderer};
|
||||
use iced_native::{
|
||||
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>
|
||||
where
|
||||
B: Backend,
|
||||
B: Backend + backend::Text,
|
||||
{
|
||||
type Style = Style;
|
||||
|
||||
|
@ -273,10 +273,10 @@ where
|
||||
self.widget.hash_layout(state);
|
||||
}
|
||||
|
||||
pub fn overlay(
|
||||
&mut self,
|
||||
pub fn overlay<'b>(
|
||||
&'b mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
) -> Option<Overlay<'b, Message, Renderer>> {
|
||||
self.widget.overlay(layout)
|
||||
}
|
||||
}
|
||||
@ -366,10 +366,12 @@ where
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, B, Renderer>> {
|
||||
) -> Option<Overlay<'_, B, Renderer>> {
|
||||
let mapper = self.mapper.clone();
|
||||
|
||||
self.widget
|
||||
.overlay(layout)
|
||||
.map(|overlay| overlay.map(self.mapper.clone()))
|
||||
.map(move |overlay| overlay.map(mapper))
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,7 +452,7 @@ where
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
) -> Option<Overlay<'_, Message, Renderer>> {
|
||||
self.element.overlay(layout)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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,
|
||||
Vector, Widget,
|
||||
};
|
||||
@ -39,10 +39,10 @@ where
|
||||
pub fn new<T: 'a>(
|
||||
state: &'a mut State,
|
||||
options: impl Into<Cow<'a, [T]>>,
|
||||
on_selected: Box<dyn Fn(T) -> Message>,
|
||||
on_selected: &'a dyn Fn(T) -> Message,
|
||||
width: u16,
|
||||
target_height: f32,
|
||||
text_size: u16,
|
||||
text_size: Option<u16>,
|
||||
padding: u16,
|
||||
style: <Renderer as self::Renderer>::Style,
|
||||
) -> Self
|
||||
@ -175,8 +175,8 @@ where
|
||||
{
|
||||
hovered_option: &'a mut Option<usize>,
|
||||
options: Cow<'a, [T]>,
|
||||
on_selected: Box<dyn Fn(T) -> Message>,
|
||||
text_size: u16,
|
||||
on_selected: &'a dyn Fn(T) -> Message,
|
||||
text_size: Option<u16>,
|
||||
padding: u16,
|
||||
style: <Renderer as self::Renderer>::Style,
|
||||
}
|
||||
@ -188,8 +188,8 @@ where
|
||||
pub fn new(
|
||||
hovered_option: &'a mut Option<usize>,
|
||||
options: impl Into<Cow<'a, [T]>>,
|
||||
on_selected: Box<dyn Fn(T) -> Message>,
|
||||
text_size: u16,
|
||||
on_selected: &'a dyn Fn(T) -> Message,
|
||||
text_size: Option<u16>,
|
||||
padding: u16,
|
||||
style: <Renderer as self::Renderer>::Style,
|
||||
) -> Self {
|
||||
@ -221,17 +221,18 @@ where
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
_renderer: &Renderer,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
use std::f32;
|
||||
|
||||
let limits = limits.width(Length::Fill).height(Length::Shrink);
|
||||
let text_size = self.text_size.unwrap_or(renderer.default_size());
|
||||
|
||||
let size = {
|
||||
let intrinsic = Size::new(
|
||||
0.0,
|
||||
f32::from(self.text_size + self.padding * 2)
|
||||
f32::from(text_size + self.padding * 2)
|
||||
* self.options.len() as f32,
|
||||
);
|
||||
|
||||
@ -253,7 +254,7 @@ where
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
messages: &mut Vec<Message>,
|
||||
_renderer: &Renderer,
|
||||
renderer: &Renderer,
|
||||
_clipboard: Option<&dyn Clipboard>,
|
||||
) {
|
||||
match event {
|
||||
@ -270,11 +271,13 @@ where
|
||||
}
|
||||
Event::Mouse(mouse::Event::CursorMoved { .. }) => {
|
||||
let bounds = layout.bounds();
|
||||
let text_size =
|
||||
self.text_size.unwrap_or(renderer.default_size());
|
||||
|
||||
if bounds.contains(cursor_position) {
|
||||
*self.hovered_option = Some(
|
||||
((cursor_position.y - bounds.y)
|
||||
/ f32::from(self.text_size + self.padding * 2))
|
||||
/ f32::from(text_size + self.padding * 2))
|
||||
as usize,
|
||||
);
|
||||
}
|
||||
@ -296,14 +299,16 @@ where
|
||||
cursor_position,
|
||||
&self.options,
|
||||
*self.hovered_option,
|
||||
self.text_size,
|
||||
self.text_size.unwrap_or(renderer.default_size()),
|
||||
self.padding,
|
||||
&self.style,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Renderer: scrollable::Renderer + container::Renderer {
|
||||
pub trait Renderer:
|
||||
scrollable::Renderer + container::Renderer + text::Renderer
|
||||
{
|
||||
type Style: Default + Clone;
|
||||
|
||||
fn decorate(
|
||||
|
@ -35,7 +35,7 @@ where
|
||||
renderer: &mut P::Renderer,
|
||||
debug: &mut Debug,
|
||||
) -> Self {
|
||||
let user_interface = build_user_interface(
|
||||
let mut user_interface = build_user_interface(
|
||||
&mut program,
|
||||
Cache::default(),
|
||||
renderer,
|
||||
@ -153,7 +153,7 @@ where
|
||||
command
|
||||
}));
|
||||
|
||||
let user_interface = build_user_interface(
|
||||
let mut user_interface = build_user_interface(
|
||||
&mut self.program,
|
||||
temp_cache,
|
||||
renderer,
|
||||
|
@ -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;
|
||||
|
||||
@ -19,13 +19,13 @@ use std::hash::Hasher;
|
||||
/// [`UserInterface`]: struct.UserInterface.html
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct UserInterface<'a, Message, Renderer> {
|
||||
base: Layer<Element<'a, Message, Renderer>>,
|
||||
overlay: Option<Layer<Overlay<'a, Message, Renderer>>>,
|
||||
root: Element<'a, Message, Renderer>,
|
||||
base: Layer,
|
||||
overlay: Option<Layer>,
|
||||
bounds: Size,
|
||||
}
|
||||
|
||||
struct Layer<T> {
|
||||
root: T,
|
||||
struct Layer {
|
||||
layout: layout::Node,
|
||||
hash: u64,
|
||||
}
|
||||
@ -97,9 +97,9 @@ where
|
||||
cache: Cache,
|
||||
renderer: &mut Renderer,
|
||||
) -> Self {
|
||||
let mut root = root.into();
|
||||
let root = root.into();
|
||||
|
||||
let (base, overlay) = {
|
||||
let base = {
|
||||
let hash = {
|
||||
let hasher = &mut crate::Hasher::default();
|
||||
root.hash_layout(hasher);
|
||||
@ -115,27 +115,13 @@ where
|
||||
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds))
|
||||
};
|
||||
|
||||
let overlay = root.overlay(Layout::new(&layout));
|
||||
|
||||
(Layer { root, layout, hash }, overlay)
|
||||
Layer { layout, hash }
|
||||
};
|
||||
|
||||
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 {
|
||||
root,
|
||||
base,
|
||||
overlay,
|
||||
overlay: None,
|
||||
bounds,
|
||||
}
|
||||
}
|
||||
@ -215,35 +201,49 @@ where
|
||||
) -> Vec<Message> {
|
||||
let mut messages = Vec::new();
|
||||
|
||||
for event in events {
|
||||
if let Some(overlay) = &mut self.overlay {
|
||||
let base_cursor =
|
||||
if overlay.layout.bounds().contains(cursor_position) {
|
||||
// TODO: Encode cursor availability
|
||||
Point::new(-1.0, -1.0)
|
||||
} else {
|
||||
cursor_position
|
||||
};
|
||||
let base_events = if let Some(mut overlay) =
|
||||
self.root.overlay(Layout::new(&self.base.layout))
|
||||
{
|
||||
let layer = {
|
||||
let new_hash = {
|
||||
let hasher = &mut crate::Hasher::default();
|
||||
overlay.hash_layout(hasher);
|
||||
|
||||
overlay.root.on_event(
|
||||
hasher.finish()
|
||||
};
|
||||
|
||||
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(),
|
||||
Layout::new(&overlay.layout),
|
||||
Layout::new(&layer.layout),
|
||||
cursor_position,
|
||||
&mut messages,
|
||||
renderer,
|
||||
clipboard,
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
self.overlay = Some(layer);
|
||||
|
||||
None
|
||||
} else {
|
||||
Some(events)
|
||||
};
|
||||
|
||||
if let Some(events) = base_events {
|
||||
for event in events {
|
||||
self.root.widget.on_event(
|
||||
event,
|
||||
Layout::new(&self.base.layout),
|
||||
cursor_position,
|
||||
@ -327,12 +327,12 @@ where
|
||||
/// }
|
||||
/// ```
|
||||
pub fn draw(
|
||||
&self,
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
cursor_position: Point,
|
||||
) -> Renderer::Output {
|
||||
if let Some(overlay) = &self.overlay {
|
||||
let overlay_bounds = overlay.layout.bounds();
|
||||
if let Some(layer) = &self.overlay {
|
||||
let overlay_bounds = layer.layout.bounds();
|
||||
|
||||
let base_cursor = if overlay_bounds.contains(cursor_position) {
|
||||
Point::new(-1.0, -1.0)
|
||||
@ -340,27 +340,33 @@ where
|
||||
cursor_position
|
||||
};
|
||||
|
||||
let base_primitives = self.base.root.widget.draw(
|
||||
let base_primitives = self.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,
|
||||
);
|
||||
if let Some(overlay) =
|
||||
self.root.overlay(Layout::new(&self.base.layout))
|
||||
{
|
||||
let overlay_primitives = overlay.draw(
|
||||
renderer,
|
||||
&Renderer::Defaults::default(),
|
||||
Layout::new(&layer.layout),
|
||||
cursor_position,
|
||||
);
|
||||
|
||||
renderer.overlay(
|
||||
base_primitives,
|
||||
overlay_primitives,
|
||||
overlay_bounds,
|
||||
)
|
||||
renderer.overlay(
|
||||
base_primitives,
|
||||
overlay_primitives,
|
||||
overlay_bounds,
|
||||
)
|
||||
} else {
|
||||
base_primitives
|
||||
}
|
||||
} else {
|
||||
self.base.root.widget.draw(
|
||||
self.root.widget.draw(
|
||||
renderer,
|
||||
&Renderer::Defaults::default(),
|
||||
Layout::new(&self.base.layout),
|
||||
|
@ -179,10 +179,10 @@ where
|
||||
) {
|
||||
}
|
||||
|
||||
fn overlay(
|
||||
&mut self,
|
||||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
_layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
) -> Option<Overlay<'b, Message, Renderer>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ where
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
) -> Option<Overlay<'_, Message, Renderer>> {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.zip(layout.children())
|
||||
|
@ -10,7 +10,7 @@ pub struct ComboBox<'a, T, Message, Renderer: self::Renderer>
|
||||
where
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
{
|
||||
internal: Option<Internal<'a, T, Message>>,
|
||||
internal: Internal<'a, T, Message>,
|
||||
options: Cow<'a, [T]>,
|
||||
selected: Option<T>,
|
||||
width: Length,
|
||||
@ -42,10 +42,10 @@ where
|
||||
on_selected: impl Fn(T) -> Message + 'static,
|
||||
) -> Self {
|
||||
Self {
|
||||
internal: Some(Internal {
|
||||
internal: Internal {
|
||||
menu: &mut state.menu,
|
||||
on_selected: Box::new(on_selected),
|
||||
}),
|
||||
},
|
||||
options: options.into(),
|
||||
selected,
|
||||
width: Length::Shrink,
|
||||
@ -180,16 +180,14 @@ where
|
||||
) {
|
||||
match event {
|
||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
|
||||
if let Some(internal) = &mut self.internal {
|
||||
if layout.bounds().contains(cursor_position) {
|
||||
let selected = self.selected.as_ref();
|
||||
if layout.bounds().contains(cursor_position) {
|
||||
let selected = self.selected.as_ref();
|
||||
|
||||
internal.menu.open(
|
||||
self.options
|
||||
.iter()
|
||||
.position(|option| Some(option) == selected),
|
||||
);
|
||||
}
|
||||
self.internal.menu.open(
|
||||
self.options
|
||||
.iter()
|
||||
.position(|option| Some(option) == selected),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -217,33 +215,23 @@ where
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
let is_open = self
|
||||
.internal
|
||||
.as_ref()
|
||||
.map(|internal| internal.menu.is_open())
|
||||
.unwrap_or(false);
|
||||
) -> Option<Overlay<'_, Message, Renderer>> {
|
||||
if self.internal.menu.is_open() {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
if is_open {
|
||||
if let Some(Internal { menu, on_selected }) = self.internal.take() {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
Some(Overlay::new(
|
||||
layout.position(),
|
||||
Box::new(Menu::new(
|
||||
menu,
|
||||
self.options.clone(),
|
||||
on_selected,
|
||||
bounds.width.round() as u16,
|
||||
bounds.height,
|
||||
self.text_size.unwrap_or(20),
|
||||
self.padding,
|
||||
Renderer::menu_style(&self.style),
|
||||
)),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Some(Overlay::new(
|
||||
layout.position(),
|
||||
Box::new(Menu::new(
|
||||
self.internal.menu,
|
||||
self.options.clone(),
|
||||
&self.internal.on_selected,
|
||||
bounds.width.round() as u16,
|
||||
bounds.height,
|
||||
self.text_size,
|
||||
self.padding,
|
||||
Renderer::menu_style(&self.style),
|
||||
)),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ where
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
) -> Option<Overlay<'_, Message, Renderer>> {
|
||||
self.content.overlay(layout.children().next().unwrap())
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ where
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
) -> Option<Overlay<'_, Message, Renderer>> {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.zip(layout.children())
|
||||
|
@ -319,14 +319,16 @@ where
|
||||
fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Overlay<'a, Message, Renderer>> {
|
||||
self.content
|
||||
) -> Option<Overlay<'_, Message, Renderer>> {
|
||||
let Self { content, state, .. } = self;
|
||||
|
||||
content
|
||||
.overlay(layout.children().next().unwrap())
|
||||
.map(|overlay| {
|
||||
let bounds = layout.bounds();
|
||||
let content_layout = layout.children().next().unwrap();
|
||||
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)))
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user