From 55dc3b5619392f4a20389255708c61082b3d4c1a Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 14:31:38 -0800 Subject: Introduce internal `overlay::Nested` for `UserInterface` --- core/src/overlay.rs | 16 +- core/src/overlay/element.rs | 27 +++- core/src/overlay/group.rs | 11 +- examples/toast/src/main.rs | 7 +- runtime/src/user_interface.rs | 71 +++++---- runtime/src/user_interface/overlay.rs | 288 ++++++++++++++++++++++++++++++++++ widget/src/lazy.rs | 9 +- widget/src/lazy/component.rs | 9 +- widget/src/lazy/responsive.rs | 9 +- 9 files changed, 406 insertions(+), 41 deletions(-) create mode 100644 runtime/src/user_interface/overlay.rs diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 1fa994e4..2e05db93 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -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> { + None + } } /// Returns a [`Group`] of overlay [`Element`] children. diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index be67fc76..edb1b443 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -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> { + 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) } } diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index e770abf8..7a38222b 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -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) + }) } } diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 395cbc10..4282ddcf 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -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)) diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 68ff6158..8ae0363a 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -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( diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs new file mode 100644 index 00000000..6dfed153 --- /dev/null +++ b/runtime/src/user_interface/overlay.rs @@ -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>, +); + +impl<'a, Message, Renderer> Inner<'a, Message, Renderer> { + fn with_element_mut( + &self, + mut f: impl FnMut(&mut overlay::Element<'_, Message, Renderer>) -> T, + ) -> T { + (f)(&mut self.0.borrow_mut()) + } +} + +impl<'a, Message, Renderer> Overlay + for Nested<'a, Message, Renderer> +where + Renderer: renderer::Renderer, +{ + fn layout( + &self, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> Vec + 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: &::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + fn recurse<'a, Message, Renderer>( + element: &mut overlay::Element<'_, Message, Renderer>, + mut layouts: impl Iterator>, + renderer: &mut Renderer, + theme: &::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, + ) { + fn recurse<'a, Message, Renderer>( + element: &mut overlay::Element<'_, Message, Renderer>, + mut layouts: impl Iterator>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) 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>, + 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>, + 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>, + 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> { + None + } +} diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 92a611c3..89376136 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -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() } diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index f462c8cf..edd0c2a2 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -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() } diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index bd6385bc..c00b8618 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -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() } -- cgit From 0a56ffb5d6260832d27c2cae70d4b8536e000001 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 13:48:10 -0800 Subject: Add nested overlay method to group & map --- core/src/overlay/element.rs | 10 ++++++++++ core/src/overlay/group.rs | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index edb1b443..c2134343 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -270,4 +270,14 @@ where ) -> bool { self.content.is_over(layout, renderer, cursor_position) } + + fn overlay<'b>( + &'b mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + self.content + .overlay(layout, renderer) + .map(|overlay| overlay.map(self.mapper)) + } } diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index 7a38222b..deffaad0 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -160,6 +160,21 @@ where child.is_over(layout, renderer, cursor_position) }) } + + fn overlay<'b>( + &'b mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + let children = self + .children + .iter_mut() + .zip(layout.children()) + .filter_map(|(child, layout)| child.overlay(layout, renderer)) + .collect::>(); + + (!children.is_empty()).then(|| Group::with_children(children).overlay()) + } } impl<'a, Message, Renderer> From> -- cgit From f608056c5029727a9523e7a245b7118eb48caa5f Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 13:49:11 -0800 Subject: Add nested picklist to modal example --- examples/modal/src/main.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 5c43c203..7fcbbfe4 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -3,11 +3,13 @@ use iced::keyboard; use iced::subscription::{self, Subscription}; use iced::theme; use iced::widget::{ - self, button, column, container, horizontal_space, row, text, text_input, + self, button, column, container, horizontal_space, pick_list, row, text, + text_input, }; use iced::{Alignment, Application, Command, Element, Event, Length, Settings}; -use self::modal::Modal; +use modal::Modal; +use std::fmt; pub fn main() -> iced::Result { App::run(Settings::default()) @@ -18,6 +20,7 @@ struct App { show_modal: bool, email: String, password: String, + plan: Plan, } #[derive(Debug, Clone)] @@ -26,6 +29,7 @@ enum Message { HideModal, Email(String), Password(String), + Plan(Plan), Submit, Event(Event), } @@ -66,6 +70,10 @@ impl Application for App { self.password = password; Command::none() } + Message::Plan(plan) => { + self.plan = plan; + Command::none() + } Message::Submit => { if !self.email.is_empty() && !self.password.is_empty() { self.hide_modal(); @@ -149,6 +157,16 @@ impl Application for App { .padding(5), ] .spacing(5), + column![ + text("Plan").size(12), + pick_list( + Plan::ALL, + Some(self.plan), + Message::Plan + ) + .padding(5), + ] + .spacing(5), button(text("Submit")).on_press(Message::HideModal), ] .spacing(10) @@ -176,6 +194,29 @@ impl App { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +enum Plan { + #[default] + Basic, + Pro, + Enterprise, +} + +impl Plan { + pub const ALL: &[Self] = &[Self::Basic, Self::Pro, Self::Enterprise]; +} + +impl fmt::Display for Plan { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Plan::Basic => "Basic", + Plan::Pro => "Pro", + Plan::Enterprise => "Enterprise", + } + .fmt(f) + } +} + mod modal { use iced::advanced::layout::{self, Layout}; use iced::advanced::overlay; @@ -469,6 +510,18 @@ mod modal { renderer, ) } + + fn overlay<'c>( + &'c mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + self.content.as_widget_mut().overlay( + self.tree, + layout.children().next().unwrap(), + renderer, + ) + } } impl<'a, Message, Renderer> From> -- cgit From f44d4292838f0eaa3fca3ce074977082bb2a6be9 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 13:49:25 -0800 Subject: Render nested in layer --- runtime/src/user_interface/overlay.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs index 6dfed153..590cc49f 100644 --- a/runtime/src/user_interface/overlay.rs +++ b/runtime/src/user_interface/overlay.rs @@ -102,7 +102,9 @@ where { let layout = layouts.next().unwrap(); - element.draw(renderer, theme, style, layout, cursor); + renderer.with_layer(layout.bounds(), |renderer| { + element.draw(renderer, theme, style, layout, cursor); + }); if let Some(mut overlay) = element.overlay(layout, renderer) { recurse(&mut overlay, layouts, renderer, theme, style, cursor); -- cgit From 1ce047cdb3bb210a8a949794d3db88b3a029df81 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 13:53:22 -0800 Subject: Prioritize mouse interaction of deepest `Overlay` --- runtime/src/user_interface/overlay.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs index 590cc49f..f80d1914 100644 --- a/runtime/src/user_interface/overlay.rs +++ b/runtime/src/user_interface/overlay.rs @@ -224,16 +224,28 @@ where { 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() - }; + if let Some(cursor_position) = cursor.position() { + match element.overlay(layout, renderer) { + Some(mut overlay) + if overlay.is_over( + layout, + renderer, + cursor_position, + ) => + { + return recurse( + &mut overlay, + layouts, + cursor, + viewport, + renderer, + ); + } + _ => {} + } + } - element - .mouse_interaction(layout, cursor, viewport, renderer) - .max(interaction) + element.mouse_interaction(layout, cursor, viewport, renderer) } self.overlay.with_element_mut(|element| { -- cgit From d4bb7c0b24e3bf7939bdecf2f3e1e0dba67587fc Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 15:06:44 -0800 Subject: Remove unwraps in `overlay::Nested` and fix `mouse_interaction` --- runtime/src/user_interface/overlay.rs | 120 +++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 51 deletions(-) diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs index f80d1914..332eb32d 100644 --- a/runtime/src/user_interface/overlay.rs +++ b/runtime/src/user_interface/overlay.rs @@ -100,14 +100,25 @@ where ) where Renderer: renderer::Renderer, { - let layout = layouts.next().unwrap(); + if let Some(layout) = layouts.next() { + renderer.with_layer(layout.bounds(), |renderer| { + element.draw(renderer, theme, style, layout, cursor); + }); - renderer.with_layer(layout.bounds(), |renderer| { - element.draw(renderer, theme, style, layout, cursor); - }); + renderer.with_layer(layout.bounds(), |renderer| { + element.draw(renderer, theme, style, layout, cursor); + }); - if let Some(mut overlay) = element.overlay(layout, renderer) { - recurse(&mut overlay, layouts, renderer, theme, style, cursor); + if let Some(mut overlay) = element.overlay(layout, renderer) { + recurse( + &mut overlay, + layouts, + renderer, + theme, + style, + cursor, + ); + } } } @@ -132,12 +143,12 @@ where ) where Renderer: renderer::Renderer, { - let layout = layouts.next().unwrap(); + if let Some(layout) = layouts.next() { + element.operate(layout, renderer, operation); - element.operate(layout, renderer, operation); - - if let Some(mut overlay) = element.overlay(layout, renderer) { - recurse(&mut overlay, layouts, renderer, operation); + if let Some(mut overlay) = element.overlay(layout, renderer) { + recurse(&mut overlay, layouts, renderer, operation); + } } } @@ -167,10 +178,10 @@ where where Renderer: renderer::Renderer, { - let layout = layouts.next().unwrap(); - - let status = - if let Some(mut overlay) = element.overlay(layout, renderer) { + if let Some(layout) = layouts.next() { + let status = if let Some(mut overlay) = + element.overlay(layout, renderer) + { recurse( &mut overlay, layouts, @@ -184,11 +195,15 @@ where event::Status::Ignored }; - if matches!(status, event::Status::Ignored) { - element - .on_event(event, layout, cursor, renderer, clipboard, shell) + if matches!(status, event::Status::Ignored) { + element.on_event( + event, layout, cursor, renderer, clipboard, shell, + ) + } else { + status + } } else { - status + event::Status::Ignored } } @@ -218,41 +233,44 @@ where cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, - ) -> mouse::Interaction + ) -> Option where Renderer: renderer::Renderer, { - let layout = layouts.next().unwrap(); + let layout = layouts.next()?; + let cursor_position = cursor.position()?; - if let Some(cursor_position) = cursor.position() { - match element.overlay(layout, renderer) { - Some(mut overlay) - if overlay.is_over( - layout, - renderer, - cursor_position, - ) => - { - return recurse( + if !element.is_over(layout, renderer, cursor_position) { + return None; + } + + Some( + element + .overlay(layout, renderer) + .and_then(|mut overlay| { + recurse( &mut overlay, layouts, cursor, viewport, renderer, - ); - } - _ => {} - } - } - - element.mouse_interaction(layout, cursor, viewport, renderer) + ) + }) + .unwrap_or_else(|| { + element.mouse_interaction( + layout, cursor, viewport, renderer, + ) + }), + ) } - self.overlay.with_element_mut(|element| { - let layouts = layout.children(); + self.overlay + .with_element_mut(|element| { + let layouts = layout.children(); - recurse(element, layouts, cursor, viewport, renderer) - }) + recurse(element, layouts, cursor, viewport, renderer) + }) + .unwrap_or_default() } fn is_over( @@ -270,16 +288,16 @@ where 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(layout) = layouts.next() { + if element.is_over(layout, renderer, cursor_position) { + return true; + } - if let Some(mut overlay) = element.overlay(layout, renderer) { - recurse(&mut overlay, layouts, renderer, cursor_position) + if let Some(mut overlay) = element.overlay(layout, renderer) { + recurse(&mut overlay, layouts, renderer, cursor_position) + } else { + false + } } else { false } -- cgit From 12c623f35bea0b76bae5e2eeb7d46390b8b9f0e4 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 15:58:07 -0800 Subject: Cursor availability by layer --- runtime/src/user_interface/overlay.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs index 332eb32d..44f64c61 100644 --- a/runtime/src/user_interface/overlay.rs +++ b/runtime/src/user_interface/overlay.rs @@ -9,6 +9,7 @@ use crate::core::{ }; use std::cell::RefCell; +use std::iter::Peekable; /// An [`Overlay`] container that displays nested overlays #[allow(missing_debug_implementations)] @@ -92,7 +93,7 @@ where ) { fn recurse<'a, Message, Renderer>( element: &mut overlay::Element<'_, Message, Renderer>, - mut layouts: impl Iterator>, + mut layouts: Peekable>>, renderer: &mut Renderer, theme: &::Theme, style: &renderer::Style, @@ -101,12 +102,33 @@ where Renderer: renderer::Renderer, { if let Some(layout) = layouts.next() { - renderer.with_layer(layout.bounds(), |renderer| { - element.draw(renderer, theme, style, layout, cursor); - }); + let is_over = cursor + .position() + .and_then(|cursor_position| { + layouts.peek().and_then(|nested_layout| { + element.overlay(layout, renderer).map(|overlay| { + overlay.is_over( + *nested_layout, + renderer, + cursor_position, + ) + }) + }) + }) + .unwrap_or_default(); renderer.with_layer(layout.bounds(), |renderer| { - element.draw(renderer, theme, style, layout, cursor); + element.draw( + renderer, + theme, + style, + layout, + if is_over { + mouse::Cursor::Unavailable + } else { + cursor + }, + ); }); if let Some(mut overlay) = element.overlay(layout, renderer) { @@ -123,7 +145,7 @@ where } self.overlay.with_element_mut(|element| { - let layouts = layout.children(); + let layouts = layout.children().peekable(); recurse(element, layouts, renderer, theme, style, cursor); }) -- cgit From 3e6f6eedcb51150b8a599530021cebdc629fddd2 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 16:08:05 -0800 Subject: Use layout with children for nesting --- runtime/src/user_interface/overlay.rs | 130 +++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs index 44f64c61..c495bb28 100644 --- a/runtime/src/user_interface/overlay.rs +++ b/runtime/src/user_interface/overlay.rs @@ -9,7 +9,6 @@ use crate::core::{ }; use std::cell::RefCell; -use std::iter::Peekable; /// An [`Overlay`] container that displays nested overlays #[allow(missing_debug_implementations)] @@ -55,7 +54,7 @@ where renderer: &Renderer, bounds: Size, position: Point, - ) -> Vec + ) -> layout::Node where Renderer: renderer::Renderer, { @@ -63,23 +62,23 @@ where let node = element.layout(renderer, bounds, translation); - if let Some(mut overlay) = + if let Some(mut nested) = element.overlay(Layout::new(&node), renderer) { - vec![node] - .into_iter() - .chain(recurse(&mut overlay, renderer, bounds, position)) - .collect() + layout::Node::with_children( + node.size(), + vec![ + node, + recurse(&mut nested, renderer, bounds, position), + ], + ) } else { - vec![node] + layout::Node::with_children(node.size(), vec![node]) } } self.overlay.with_element_mut(|element| { - layout::Node::with_children( - bounds, - recurse(element, renderer, bounds, position), - ) + recurse(element, renderer, bounds, position) }) } @@ -91,9 +90,9 @@ where layout: Layout<'_>, cursor: mouse::Cursor, ) { - fn recurse<'a, Message, Renderer>( + fn recurse( element: &mut overlay::Element<'_, Message, Renderer>, - mut layouts: Peekable>>, + layout: Layout<'_>, renderer: &mut Renderer, theme: &::Theme, style: &renderer::Style, @@ -101,18 +100,21 @@ where ) where Renderer: renderer::Renderer, { + let mut layouts = layout.children(); + if let Some(layout) = layouts.next() { + let nested_layout = layouts.next(); + let is_over = cursor .position() - .and_then(|cursor_position| { - layouts.peek().and_then(|nested_layout| { - element.overlay(layout, renderer).map(|overlay| { - overlay.is_over( - *nested_layout, - renderer, - cursor_position, - ) - }) + .zip(nested_layout) + .and_then(|(cursor_position, nested_layout)| { + element.overlay(layout, renderer).map(|nested| { + nested.is_over( + nested_layout, + renderer, + cursor_position, + ) }) }) .unwrap_or_default(); @@ -131,10 +133,12 @@ where ); }); - if let Some(mut overlay) = element.overlay(layout, renderer) { + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(nested_layout) + { recurse( - &mut overlay, - layouts, + &mut nested, + nested_layout, renderer, theme, style, @@ -145,9 +149,7 @@ where } self.overlay.with_element_mut(|element| { - let layouts = layout.children().peekable(); - - recurse(element, layouts, renderer, theme, style, cursor); + recurse(element, layout, renderer, theme, style, cursor); }) } @@ -157,26 +159,28 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - fn recurse<'a, Message, Renderer>( + fn recurse( element: &mut overlay::Element<'_, Message, Renderer>, - mut layouts: impl Iterator>, + layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) where Renderer: renderer::Renderer, { + let mut layouts = layout.children(); + if let Some(layout) = layouts.next() { element.operate(layout, renderer, operation); - if let Some(mut overlay) = element.overlay(layout, renderer) { - recurse(&mut overlay, layouts, renderer, operation); + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse(&mut nested, nested_layout, renderer, operation); } } } - let layouts = layout.children(); - - recurse(self.overlay.0.get_mut(), layouts, renderer, operation) + recurse(self.overlay.0.get_mut(), layout, renderer, operation) } fn on_event( @@ -188,9 +192,9 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { - fn recurse<'a, Message, Renderer>( + fn recurse( element: &mut overlay::Element<'_, Message, Renderer>, - mut layouts: impl Iterator>, + layout: Layout<'_>, event: Event, cursor: mouse::Cursor, renderer: &Renderer, @@ -200,13 +204,15 @@ where where Renderer: renderer::Renderer, { + let mut layouts = layout.children(); + if let Some(layout) = layouts.next() { - let status = if let Some(mut overlay) = - element.overlay(layout, renderer) + let status = if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) { recurse( - &mut overlay, - layouts, + &mut nested, + nested_layout, event.clone(), cursor, renderer, @@ -229,11 +235,9 @@ where } } - let layouts = layout.children(); - recurse( self.overlay.0.get_mut(), - layouts, + layout, event, cursor, renderer, @@ -249,9 +253,9 @@ where viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - fn recurse<'a, Message, Renderer>( + fn recurse( element: &mut overlay::Element<'_, Message, Renderer>, - mut layouts: impl Iterator>, + layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, @@ -259,6 +263,8 @@ where where Renderer: renderer::Renderer, { + let mut layouts = layout.children(); + let layout = layouts.next()?; let cursor_position = cursor.position()?; @@ -269,10 +275,11 @@ where Some( element .overlay(layout, renderer) - .and_then(|mut overlay| { + .zip(layouts.next()) + .and_then(|(mut overlay, layout)| { recurse( &mut overlay, - layouts, + layout, cursor, viewport, renderer, @@ -288,9 +295,7 @@ where self.overlay .with_element_mut(|element| { - let layouts = layout.children(); - - recurse(element, layouts, cursor, viewport, renderer) + recurse(element, layout, cursor, viewport, renderer) }) .unwrap_or_default() } @@ -301,22 +306,31 @@ where renderer: &Renderer, cursor_position: Point, ) -> bool { - fn recurse<'a, Message, Renderer>( + fn recurse( element: &mut overlay::Element<'_, Message, Renderer>, - mut layouts: impl Iterator>, + layout: Layout<'_>, renderer: &Renderer, cursor_position: Point, ) -> bool where Renderer: renderer::Renderer, { + let mut layouts = layout.children(); + if let Some(layout) = layouts.next() { if element.is_over(layout, renderer, cursor_position) { return true; } - if let Some(mut overlay) = element.overlay(layout, renderer) { - recurse(&mut overlay, layouts, renderer, cursor_position) + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse( + &mut nested, + nested_layout, + renderer, + cursor_position, + ) } else { false } @@ -326,9 +340,7 @@ where } self.overlay.with_element_mut(|element| { - let layouts = layout.children(); - - recurse(element, layouts, renderer, cursor_position) + recurse(element, layout, renderer, cursor_position) }) } -- cgit From 83140d6049c165020c2afc1db303b2556e40488e Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 19:03:52 -0800 Subject: Remove interior mutability Nested doesn't need to implement Overlay trait, it can be be used mutably in user interface so we don't need interior mutability. --- runtime/src/user_interface.rs | 37 +++++++-------- runtime/src/user_interface/overlay.rs | 85 ++++++++++------------------------- 2 files changed, 42 insertions(+), 80 deletions(-) diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 8ae0363a..1d55970e 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -8,7 +8,7 @@ use crate::core::renderer; use crate::core::widget; use crate::core::window; use crate::core::{Clipboard, Point, Rectangle, Size}; -use crate::core::{Element, Layout, Overlay, Shell}; +use crate::core::{Element, Layout, Shell}; /// A set of interactive graphical elements with a specific [`Layout`]. /// @@ -261,22 +261,23 @@ where } } - let base_cursor = manual_overlay - .as_ref() - .filter(|overlay| { - cursor - .position() - .map(|cursor_position| { - overlay.is_over( - Layout::new(&layout), - renderer, - cursor_position, - ) - }) - .unwrap_or_default() + let base_cursor = if manual_overlay + .as_mut() + .and_then(|overlay| { + cursor.position().map(|cursor_position| { + overlay.is_over( + Layout::new(&layout), + renderer, + cursor_position, + ) + }) }) - .map(|_| mouse::Cursor::Unavailable) - .unwrap_or(cursor); + .unwrap_or_default() + { + mouse::Cursor::Unavailable + } else { + cursor + }; self.overlay = Some(layout); @@ -434,7 +435,7 @@ where let viewport = Rectangle::with_size(self.bounds); - let base_cursor = if let Some(overlay) = self + let base_cursor = if let Some(mut overlay) = self .root .as_widget_mut() .overlay(&mut self.state, Layout::new(&self.base), renderer) @@ -503,7 +504,7 @@ where root.as_widget_mut() .overlay(&mut self.state, Layout::new(base), renderer) .map(overlay::Nested::new) - .map(|overlay| { + .map(|mut overlay| { let overlay_interaction = overlay.mouse_interaction( Layout::new(layout), cursor, diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs index c495bb28..24ddb649 100644 --- a/runtime/src/user_interface/overlay.rs +++ b/runtime/src/user_interface/overlay.rs @@ -4,47 +4,25 @@ 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; +use crate::core::{Clipboard, Event, Layout, Point, Rectangle, Shell, Size}; /// An [`Overlay`] container that displays nested overlays #[allow(missing_debug_implementations)] pub struct Nested<'a, Message, Renderer> { - overlay: Inner<'a, Message, Renderer>, + overlay: overlay::Element<'a, Message, Renderer>, } -impl<'a, Message, Renderer> Nested<'a, Message, Renderer> { +impl<'a, Message, Renderer> Nested<'a, Message, Renderer> +where + Renderer: renderer::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)), - } + Self { overlay: element } } -} - -struct Inner<'a, Message, Renderer>( - RefCell>, -); - -impl<'a, Message, Renderer> Inner<'a, Message, Renderer> { - fn with_element_mut( - &self, - mut f: impl FnMut(&mut overlay::Element<'_, Message, Renderer>) -> T, - ) -> T { - (f)(&mut self.0.borrow_mut()) - } -} -impl<'a, Message, Renderer> Overlay - for Nested<'a, Message, Renderer> -where - Renderer: renderer::Renderer, -{ - fn layout( - &self, + pub fn layout( + &mut self, renderer: &Renderer, bounds: Size, position: Point, @@ -77,13 +55,11 @@ where } } - self.overlay.with_element_mut(|element| { - recurse(element, renderer, bounds, position) - }) + recurse(&mut self.overlay, renderer, bounds, position) } - fn draw( - &self, + pub fn draw( + &mut self, renderer: &mut Renderer, theme: &::Theme, style: &renderer::Style, @@ -148,12 +124,10 @@ where } } - self.overlay.with_element_mut(|element| { - recurse(element, layout, renderer, theme, style, cursor); - }) + recurse(&mut self.overlay, layout, renderer, theme, style, cursor); } - fn operate( + pub fn operate( &mut self, layout: Layout<'_>, renderer: &Renderer, @@ -180,10 +154,10 @@ where } } - recurse(self.overlay.0.get_mut(), layout, renderer, operation) + recurse(&mut self.overlay, layout, renderer, operation) } - fn on_event( + pub fn on_event( &mut self, event: Event, layout: Layout<'_>, @@ -236,7 +210,7 @@ where } recurse( - self.overlay.0.get_mut(), + &mut self.overlay, layout, event, cursor, @@ -246,8 +220,8 @@ where ) } - fn mouse_interaction( - &self, + pub fn mouse_interaction( + &mut self, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, @@ -293,15 +267,12 @@ where ) } - self.overlay - .with_element_mut(|element| { - recurse(element, layout, cursor, viewport, renderer) - }) + recurse(&mut self.overlay, layout, cursor, viewport, renderer) .unwrap_or_default() } - fn is_over( - &self, + pub fn is_over( + &mut self, layout: Layout<'_>, renderer: &Renderer, cursor_position: Point, @@ -339,16 +310,6 @@ where } } - self.overlay.with_element_mut(|element| { - recurse(element, layout, renderer, cursor_position) - }) - } - - fn overlay<'b>( - &'b mut self, - _layout: Layout<'_>, - _renderer: &Renderer, - ) -> Option> { - None + recurse(&mut self.overlay, layout, renderer, cursor_position) } } -- cgit From 4de6ee6fa18be5b8fa511bffe93bbec2c5811bfc Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 19:11:50 -0800 Subject: Cursor availability during on_event --- runtime/src/user_interface/overlay.rs | 73 ++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs index 24ddb649..1211d55b 100644 --- a/runtime/src/user_interface/overlay.rs +++ b/runtime/src/user_interface/overlay.rs @@ -174,42 +174,67 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - ) -> event::Status + ) -> (event::Status, bool) where Renderer: renderer::Renderer, { let mut layouts = layout.children(); if let Some(layout) = layouts.next() { - let status = if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse( - &mut nested, - nested_layout, - event.clone(), - cursor, - renderer, - clipboard, - shell, - ) - } else { - event::Status::Ignored - }; + let (nested_status, nested_is_over) = + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse( + &mut nested, + nested_layout, + event.clone(), + cursor, + renderer, + clipboard, + shell, + ) + } else { + (event::Status::Ignored, false) + }; - if matches!(status, event::Status::Ignored) { - element.on_event( - event, layout, cursor, renderer, clipboard, shell, + if matches!(nested_status, event::Status::Ignored) { + let is_over = nested_is_over + || cursor + .position() + .map(|cursor_position| { + element.is_over( + layout, + renderer, + cursor_position, + ) + }) + .unwrap_or_default(); + + ( + element.on_event( + event, + layout, + if nested_is_over { + mouse::Cursor::Unavailable + } else { + cursor + }, + renderer, + clipboard, + shell, + ), + is_over, ) } else { - status + (nested_status, nested_is_over) } } else { - event::Status::Ignored + (event::Status::Ignored, false) } } - recurse( + let (status, _) = recurse( &mut self.overlay, layout, event, @@ -217,7 +242,9 @@ where renderer, clipboard, shell, - ) + ); + + status } pub fn mouse_interaction( -- cgit From b0205e03d8e4794850e55e8c4bf83a40dd41aa9d Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sun, 19 Feb 2023 17:43:13 -0800 Subject: Use nested for lazy widgets --- runtime/src/lib.rs | 1 + runtime/src/overlay.rs | 4 + runtime/src/overlay/nested.rs | 353 ++++++++++++++++++++++++++++++++++ runtime/src/user_interface.rs | 6 +- runtime/src/user_interface/overlay.rs | 342 -------------------------------- widget/src/lazy.rs | 33 ++-- widget/src/lazy/component.rs | 42 ++-- widget/src/lazy/responsive.rs | 27 +-- 8 files changed, 418 insertions(+), 390 deletions(-) create mode 100644 runtime/src/overlay.rs create mode 100644 runtime/src/overlay/nested.rs delete mode 100644 runtime/src/user_interface/overlay.rs diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 50abf7b2..4bbf9687 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -47,6 +47,7 @@ pub mod clipboard; pub mod command; pub mod font; pub mod keyboard; +pub mod overlay; pub mod program; pub mod system; pub mod user_interface; diff --git a/runtime/src/overlay.rs b/runtime/src/overlay.rs new file mode 100644 index 00000000..03390980 --- /dev/null +++ b/runtime/src/overlay.rs @@ -0,0 +1,4 @@ +//! Overlays for user interfaces. +mod nested; + +pub use nested::Nested; diff --git a/runtime/src/overlay/nested.rs b/runtime/src/overlay/nested.rs new file mode 100644 index 00000000..5c5fafde --- /dev/null +++ b/runtime/src/overlay/nested.rs @@ -0,0 +1,353 @@ +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, Point, Rectangle, Shell, Size}; + +/// An [`Overlay`] container that displays nested overlays +#[allow(missing_debug_implementations)] +pub struct Nested<'a, Message, Renderer> { + overlay: overlay::Element<'a, Message, Renderer>, +} + +impl<'a, Message, Renderer> Nested<'a, Message, Renderer> +where + Renderer: renderer::Renderer, +{ + /// Creates a nested overlay from the provided [`overlay::Element`] + pub fn new(element: overlay::Element<'a, Message, Renderer>) -> Self { + Self { overlay: element } + } + + /// Returns the position of the [`Nested`] overlay. + pub fn position(&self) -> Point { + self.overlay.position() + } + + /// Returns the layout [`Node`] of the [`Nested`] overlay. + pub fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node + where + Renderer: renderer::Renderer, + { + let translation = position - element.position(); + + let node = element.layout(renderer, bounds, translation); + + if let Some(mut nested) = + element.overlay(Layout::new(&node), renderer) + { + layout::Node::with_children( + node.size(), + vec![ + node, + recurse(&mut nested, renderer, bounds, position), + ], + ) + } else { + layout::Node::with_children(node.size(), vec![node]) + } + } + + recurse(&mut self.overlay, renderer, bounds, position) + } + + /// Draws the [`Nested`] overlay using the associated `Renderer`. + pub fn draw( + &mut self, + renderer: &mut Renderer, + theme: &::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + renderer: &mut Renderer, + theme: &::Theme, + style: &renderer::Style, + cursor: mouse::Cursor, + ) where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + let nested_layout = layouts.next(); + + let is_over = cursor + .position() + .zip(nested_layout) + .and_then(|(cursor_position, nested_layout)| { + element.overlay(layout, renderer).map(|nested| { + nested.is_over( + nested_layout, + renderer, + cursor_position, + ) + }) + }) + .unwrap_or_default(); + + renderer.with_layer(layout.bounds(), |renderer| { + element.draw( + renderer, + theme, + style, + layout, + if is_over { + mouse::Cursor::Unavailable + } else { + cursor + }, + ); + }); + + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(nested_layout) + { + recurse( + &mut nested, + nested_layout, + renderer, + theme, + style, + cursor, + ); + } + } + } + + recurse(&mut self.overlay, layout, renderer, theme, style, cursor); + } + + /// Applies a [`widget::Operation`] to the [`Nested`] overlay. + pub fn operate( + &mut self, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + element.operate(layout, renderer, operation); + + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse(&mut nested, nested_layout, renderer, operation); + } + } + } + + recurse(&mut self.overlay, layout, renderer, operation) + } + + /// Processes a runtime [`Event`]. + pub 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( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + event: Event, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> (event::Status, bool) + where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + let (nested_status, nested_is_over) = + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse( + &mut nested, + nested_layout, + event.clone(), + cursor, + renderer, + clipboard, + shell, + ) + } else { + (event::Status::Ignored, false) + }; + + if matches!(nested_status, event::Status::Ignored) { + let is_over = nested_is_over + || cursor + .position() + .map(|cursor_position| { + element.is_over( + layout, + renderer, + cursor_position, + ) + }) + .unwrap_or_default(); + + ( + element.on_event( + event, + layout, + if nested_is_over { + mouse::Cursor::Unavailable + } else { + cursor + }, + renderer, + clipboard, + shell, + ), + is_over, + ) + } else { + (nested_status, nested_is_over) + } + } else { + (event::Status::Ignored, false) + } + } + + let (status, _) = recurse( + &mut self.overlay, + layout, + event, + cursor, + renderer, + clipboard, + shell, + ); + + status + } + + /// Returns the current [`mouse::Interaction`] of the [`Nested`] overlay. + pub fn mouse_interaction( + &mut self, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> Option + where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + let layout = layouts.next()?; + let cursor_position = cursor.position()?; + + if !element.is_over(layout, renderer, cursor_position) { + return None; + } + + Some( + element + .overlay(layout, renderer) + .zip(layouts.next()) + .and_then(|(mut overlay, layout)| { + recurse( + &mut overlay, + layout, + cursor, + viewport, + renderer, + ) + }) + .unwrap_or_else(|| { + element.mouse_interaction( + layout, cursor, viewport, renderer, + ) + }), + ) + } + + recurse(&mut self.overlay, layout, cursor, viewport, renderer) + .unwrap_or_default() + } + + /// Returns true if the cursor is over the [`Nested`] overlay. + pub fn is_over( + &mut self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { + fn recurse( + element: &mut overlay::Element<'_, Message, Renderer>, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool + where + Renderer: renderer::Renderer, + { + let mut layouts = layout.children(); + + if let Some(layout) = layouts.next() { + if element.is_over(layout, renderer, cursor_position) { + return true; + } + + if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse( + &mut nested, + nested_layout, + renderer, + cursor_position, + ) + } else { + false + } + } else { + false + } + } + + recurse(&mut self.overlay, layout, renderer, cursor_position) + } +} diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 1d55970e..619423fd 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -1,14 +1,12 @@ //! 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, Point, Rectangle, Size}; -use crate::core::{Element, Layout, Shell}; +use crate::core::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; +use crate::overlay; /// A set of interactive graphical elements with a specific [`Layout`]. /// diff --git a/runtime/src/user_interface/overlay.rs b/runtime/src/user_interface/overlay.rs deleted file mode 100644 index 1211d55b..00000000 --- a/runtime/src/user_interface/overlay.rs +++ /dev/null @@ -1,342 +0,0 @@ -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, Point, Rectangle, Shell, Size}; - -/// An [`Overlay`] container that displays nested overlays -#[allow(missing_debug_implementations)] -pub struct Nested<'a, Message, Renderer> { - overlay: overlay::Element<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Nested<'a, Message, Renderer> -where - Renderer: renderer::Renderer, -{ - /// Creates a nested overlay from the provided [`overlay::Element`] - pub fn new(element: overlay::Element<'a, Message, Renderer>) -> Self { - Self { overlay: element } - } - - pub fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - position: Point, - ) -> layout::Node { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - renderer: &Renderer, - bounds: Size, - position: Point, - ) -> layout::Node - where - Renderer: renderer::Renderer, - { - let translation = position - Point::ORIGIN; - - let node = element.layout(renderer, bounds, translation); - - if let Some(mut nested) = - element.overlay(Layout::new(&node), renderer) - { - layout::Node::with_children( - node.size(), - vec![ - node, - recurse(&mut nested, renderer, bounds, position), - ], - ) - } else { - layout::Node::with_children(node.size(), vec![node]) - } - } - - recurse(&mut self.overlay, renderer, bounds, position) - } - - pub fn draw( - &mut self, - renderer: &mut Renderer, - theme: &::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - renderer: &mut Renderer, - theme: &::Theme, - style: &renderer::Style, - cursor: mouse::Cursor, - ) where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - let nested_layout = layouts.next(); - - let is_over = cursor - .position() - .zip(nested_layout) - .and_then(|(cursor_position, nested_layout)| { - element.overlay(layout, renderer).map(|nested| { - nested.is_over( - nested_layout, - renderer, - cursor_position, - ) - }) - }) - .unwrap_or_default(); - - renderer.with_layer(layout.bounds(), |renderer| { - element.draw( - renderer, - theme, - style, - layout, - if is_over { - mouse::Cursor::Unavailable - } else { - cursor - }, - ); - }); - - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(nested_layout) - { - recurse( - &mut nested, - nested_layout, - renderer, - theme, - style, - cursor, - ); - } - } - } - - recurse(&mut self.overlay, layout, renderer, theme, style, cursor); - } - - pub fn operate( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - element.operate(layout, renderer, operation); - - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse(&mut nested, nested_layout, renderer, operation); - } - } - } - - recurse(&mut self.overlay, layout, renderer, operation) - } - - pub 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( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - event: Event, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> (event::Status, bool) - where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - let (nested_status, nested_is_over) = - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse( - &mut nested, - nested_layout, - event.clone(), - cursor, - renderer, - clipboard, - shell, - ) - } else { - (event::Status::Ignored, false) - }; - - if matches!(nested_status, event::Status::Ignored) { - let is_over = nested_is_over - || cursor - .position() - .map(|cursor_position| { - element.is_over( - layout, - renderer, - cursor_position, - ) - }) - .unwrap_or_default(); - - ( - element.on_event( - event, - layout, - if nested_is_over { - mouse::Cursor::Unavailable - } else { - cursor - }, - renderer, - clipboard, - shell, - ), - is_over, - ) - } else { - (nested_status, nested_is_over) - } - } else { - (event::Status::Ignored, false) - } - } - - let (status, _) = recurse( - &mut self.overlay, - layout, - event, - cursor, - renderer, - clipboard, - shell, - ); - - status - } - - pub fn mouse_interaction( - &mut self, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - renderer: &Renderer, - ) -> Option - where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - let layout = layouts.next()?; - let cursor_position = cursor.position()?; - - if !element.is_over(layout, renderer, cursor_position) { - return None; - } - - Some( - element - .overlay(layout, renderer) - .zip(layouts.next()) - .and_then(|(mut overlay, layout)| { - recurse( - &mut overlay, - layout, - cursor, - viewport, - renderer, - ) - }) - .unwrap_or_else(|| { - element.mouse_interaction( - layout, cursor, viewport, renderer, - ) - }), - ) - } - - recurse(&mut self.overlay, layout, cursor, viewport, renderer) - .unwrap_or_default() - } - - pub fn is_over( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - cursor_position: Point, - ) -> bool { - fn recurse( - element: &mut overlay::Element<'_, Message, Renderer>, - layout: Layout<'_>, - renderer: &Renderer, - cursor_position: Point, - ) -> bool - where - Renderer: renderer::Renderer, - { - let mut layouts = layout.children(); - - if let Some(layout) = layouts.next() { - if element.is_over(layout, renderer, cursor_position) { - return true; - } - - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse( - &mut nested, - nested_layout, - renderer, - cursor_position, - ) - } else { - false - } - } else { - false - } - } - - recurse(&mut self.overlay, layout, renderer, cursor_position) - } -} diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 89376136..4903d0ed 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -20,6 +20,7 @@ use crate::core::Element; use crate::core::{ self, Clipboard, Hasher, Length, Point, Rectangle, Shell, Size, }; +use crate::runtime::overlay::Nested; use ouroboros::self_referencing; use std::cell::RefCell; @@ -260,14 +261,17 @@ where .unwrap(), tree: &mut tree.children[0], overlay_builder: |element, tree| { - element.as_widget_mut().overlay(tree, layout, renderer) + element + .as_widget_mut() + .overlay(tree, layout, renderer) + .map(|overlay| RefCell::new(Nested::new(overlay))) }, } .build(), )); - let has_overlay = overlay - .with_overlay_maybe(|overlay| overlay::Element::position(overlay)); + let has_overlay = + overlay.with_overlay_maybe(|overlay| overlay.position()); has_overlay .map(|position| overlay::Element::new(position, Box::new(overlay))) @@ -285,8 +289,8 @@ where tree: &'a mut Tree, #[borrows(mut element, mut tree)] - #[covariant] - overlay: Option>, + #[not_covariant] + overlay: Option>>, } struct Overlay<'a, Message, Renderer>(Option>); @@ -301,19 +305,20 @@ impl<'a, Message, Renderer> Drop for Overlay<'a, Message, Renderer> { impl<'a, Message, Renderer> Overlay<'a, Message, Renderer> { fn with_overlay_maybe( &self, - f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option { - self.0.as_ref().unwrap().borrow_overlay().as_ref().map(f) + self.0.as_ref().unwrap().with_overlay(|overlay| { + overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) + }) } fn with_overlay_mut_maybe( &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option { - self.0 - .as_mut() - .unwrap() - .with_overlay_mut(|overlay| overlay.as_mut().map(f)) + self.0.as_mut().unwrap().with_overlay_mut(|overlay| { + overlay.as_mut().map(|nested| (f)(nested.get_mut())) + }) } } @@ -329,9 +334,7 @@ where position: Point, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - let translation = position - overlay.position(); - - overlay.layout(renderer, bounds, translation) + overlay.layout(renderer, bounds, position) }) .unwrap_or_default() } diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index edd0c2a2..f955d9dd 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -9,6 +9,7 @@ use crate::core::widget::tree::{self, Tree}; use crate::core::{ self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; +use crate::runtime::overlay::Nested; use ouroboros::self_referencing; use std::cell::RefCell; @@ -455,11 +456,18 @@ where overlay_builder: |instance, tree| { instance.state.get_mut().as_mut().unwrap().with_element_mut( move |element| { - element.as_mut().unwrap().as_widget_mut().overlay( - &mut tree.children[0], - layout, - renderer, - ) + element + .as_mut() + .unwrap() + .as_widget_mut() + .overlay( + &mut tree.children[0], + layout, + renderer, + ) + .map(|overlay| { + RefCell::new(Nested::new(overlay)) + }) }, ) }, @@ -468,7 +476,7 @@ where )); let has_overlay = overlay.0.as_ref().unwrap().with_overlay(|overlay| { - overlay.as_ref().map(overlay::Element::position) + overlay.as_ref().map(|nested| nested.borrow().position()) }); has_overlay.map(|position| { @@ -503,8 +511,8 @@ struct Inner<'a, 'b, Message, Renderer, Event, S> { types: PhantomData<(Message, Event, S)>, #[borrows(mut instance, mut tree)] - #[covariant] - overlay: Option>, + #[not_covariant] + overlay: Option>>, } struct OverlayInstance<'a, 'b, Message, Renderer, Event, S> { @@ -516,7 +524,7 @@ impl<'a, 'b, Message, Renderer, Event, S> { fn with_overlay_maybe( &self, - f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Event, Renderer>) -> T, ) -> Option { self.overlay .as_ref() @@ -524,14 +532,14 @@ impl<'a, 'b, Message, Renderer, Event, S> .0 .as_ref() .unwrap() - .borrow_overlay() - .as_ref() - .map(f) + .with_overlay(|overlay| { + overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) + }) } fn with_overlay_mut_maybe( &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Event, Renderer>) -> T, ) -> Option { self.overlay .as_mut() @@ -539,7 +547,9 @@ impl<'a, 'b, Message, Renderer, Event, S> .0 .as_mut() .unwrap() - .with_overlay_mut(|overlay| overlay.as_mut().map(f)) + .with_overlay_mut(|overlay| { + overlay.as_mut().map(|nested| (f)(nested.get_mut())) + }) } } @@ -556,9 +566,7 @@ where position: Point, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - let translation = position - overlay.position(); - - overlay.layout(renderer, bounds, translation) + overlay.layout(renderer, bounds, position) }) .unwrap_or_default() } diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index c00b8618..07300857 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -9,6 +9,7 @@ use crate::core::{ self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; use crate::horizontal_space; +use crate::runtime::overlay::Nested; use ouroboros::self_referencing; use std::cell::{RefCell, RefMut}; @@ -298,13 +299,13 @@ where element .as_widget_mut() .overlay(tree, content_layout, renderer) + .map(|overlay| RefCell::new(Nested::new(overlay))) }, } .build(); - let has_overlay = overlay.with_overlay(|overlay| { - overlay.as_ref().map(overlay::Element::position) - }); + let has_overlay = + overlay.with_overlay_maybe(|overlay| overlay.position()); has_overlay .map(|position| overlay::Element::new(position, Box::new(overlay))) @@ -329,23 +330,27 @@ struct Overlay<'a, 'b, Message, Renderer> { types: PhantomData, #[borrows(mut content, mut tree)] - #[covariant] - overlay: Option>, + #[not_covariant] + overlay: Option>>, } impl<'a, 'b, Message, Renderer> Overlay<'a, 'b, Message, Renderer> { fn with_overlay_maybe( &self, - f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option { - self.borrow_overlay().as_ref().map(f) + self.with_overlay(|overlay| { + overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) + }) } fn with_overlay_mut_maybe( &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option { - self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) + self.with_overlay_mut(|overlay| { + overlay.as_mut().map(|nested| (f)(nested.get_mut())) + }) } } @@ -361,9 +366,7 @@ where position: Point, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - let translation = position - overlay.position(); - - overlay.layout(renderer, bounds, translation) + overlay.layout(renderer, bounds, position) }) .unwrap_or_default() } -- cgit From 87db76a11ffd0e3e2350c2b2531fde1c56ffeea6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 14 Jun 2023 11:25:05 +0200 Subject: Make `overlay::Menu` publish messages on selection --- widget/src/overlay/menu.rs | 40 +++++++++++++++++++++++----------------- widget/src/pick_list.rs | 44 +++++++++++++++++++------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index b699def0..ccf4dfb5 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -19,7 +19,7 @@ pub use iced_style::menu::{Appearance, StyleSheet}; /// A list of selectable options. #[allow(missing_debug_implementations)] -pub struct Menu<'a, T, Renderer = crate::Renderer> +pub struct Menu<'a, T, Message, Renderer = crate::Renderer> where Renderer: text::Renderer, Renderer::Theme: StyleSheet, @@ -27,7 +27,7 @@ where state: &'a mut State, options: &'a [T], hovered_option: &'a mut Option, - last_selection: &'a mut Option, + on_selected: Box Message + 'a>, width: f32, padding: Padding, text_size: Option, @@ -37,9 +37,10 @@ where style: ::Style, } -impl<'a, T, Renderer> Menu<'a, T, Renderer> +impl<'a, T, Message, Renderer> Menu<'a, T, Message, Renderer> where T: ToString + Clone, + Message: 'a, Renderer: text::Renderer + 'a, Renderer::Theme: StyleSheet + container::StyleSheet + scrollable::StyleSheet, @@ -50,13 +51,13 @@ where state: &'a mut State, options: &'a [T], hovered_option: &'a mut Option, - last_selection: &'a mut Option, + on_selected: impl FnMut(T) -> Message + 'a, ) -> Self { Menu { state, options, hovered_option, - last_selection, + on_selected: Box::new(on_selected), width: 0.0, padding: Padding::ZERO, text_size: None, @@ -121,7 +122,7 @@ where /// The `target_height` will be used to display the menu either on top /// of the target or under it, depending on the screen position and the /// dimensions of the [`Menu`]. - pub fn overlay( + pub fn overlay( self, position: Point, target_height: f32, @@ -174,7 +175,10 @@ where Renderer::Theme: StyleSheet + container::StyleSheet + scrollable::StyleSheet, { - pub fn new(menu: Menu<'a, T, Renderer>, target_height: f32) -> Self + pub fn new( + menu: Menu<'a, T, Message, Renderer>, + target_height: f32, + ) -> Self where T: Clone + ToString, { @@ -182,7 +186,7 @@ where state, options, hovered_option, - last_selection, + on_selected, width, padding, font, @@ -195,7 +199,7 @@ where let container = Container::new(Scrollable::new(List { options, hovered_option, - last_selection, + on_selected, font, text_size, text_line_height, @@ -306,14 +310,14 @@ where } } -struct List<'a, T, Renderer> +struct List<'a, T, Message, Renderer> where Renderer: text::Renderer, Renderer::Theme: StyleSheet, { options: &'a [T], hovered_option: &'a mut Option, - last_selection: &'a mut Option, + on_selected: Box Message + 'a>, padding: Padding, text_size: Option, text_line_height: text::LineHeight, @@ -323,7 +327,7 @@ where } impl<'a, T, Message, Renderer> Widget - for List<'a, T, Renderer> + for List<'a, T, Message, Renderer> where T: Clone + ToString, Renderer: text::Renderer, @@ -372,14 +376,15 @@ where cursor: mouse::Cursor, renderer: &Renderer, _clipboard: &mut dyn Clipboard, - _shell: &mut Shell<'_, Message>, + shell: &mut Shell<'_, Message>, ) -> event::Status { match event { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { if cursor.is_over(layout.bounds()) { if let Some(index) = *self.hovered_option { if let Some(option) = self.options.get(index) { - *self.last_selection = Some(option.clone()); + shell.publish((self.on_selected)(option.clone())); + return event::Status::Captured; } } } @@ -417,7 +422,8 @@ where if let Some(index) = *self.hovered_option { if let Some(option) = self.options.get(index) { - *self.last_selection = Some(option.clone()); + shell.publish((self.on_selected)(option.clone())); + return event::Status::Captured; } } } @@ -521,7 +527,7 @@ where } } -impl<'a, T, Message, Renderer> From> +impl<'a, T, Message, Renderer> From> for Element<'a, Message, Renderer> where T: ToString + Clone, @@ -529,7 +535,7 @@ where Renderer: 'a + text::Renderer, Renderer::Theme: StyleSheet, { - fn from(list: List<'a, T, Renderer>) -> Self { + fn from(list: List<'a, T, Message, Renderer>) -> Self { Element::new(list) } } diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index dc53a9c8..832aae6b 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -157,11 +157,11 @@ where From<::Style>, { fn tag(&self) -> tree::Tag { - tree::Tag::of::>() + tree::Tag::of::() } fn state(&self) -> tree::State { - tree::State::new(State::::new()) + tree::State::new(State::new()) } fn width(&self) -> Length { @@ -209,7 +209,7 @@ where self.on_selected.as_ref(), self.selected.as_ref(), &self.options, - || tree.state.downcast_mut::>(), + || tree.state.downcast_mut::(), ) } @@ -249,7 +249,7 @@ where self.selected.as_ref(), &self.handle, &self.style, - || tree.state.downcast_ref::>(), + || tree.state.downcast_ref::(), ) } @@ -259,7 +259,7 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - let state = tree.state.downcast_mut::>(); + let state = tree.state.downcast_mut::(); overlay( layout, @@ -269,6 +269,7 @@ where self.text_shaping, self.font.unwrap_or_else(|| renderer.default_font()), &self.options, + &self.on_selected, self.style.clone(), ) } @@ -295,15 +296,14 @@ where /// The local state of a [`PickList`]. #[derive(Debug)] -pub struct State { +pub struct State { menu: menu::State, keyboard_modifiers: keyboard::Modifiers, is_open: bool, hovered_option: Option, - last_selection: Option, } -impl State { +impl State { /// Creates a new [`State`] for a [`PickList`]. pub fn new() -> Self { Self { @@ -311,12 +311,11 @@ impl State { keyboard_modifiers: keyboard::Modifiers::default(), is_open: bool::default(), hovered_option: Option::default(), - last_selection: Option::default(), } } } -impl Default for State { +impl Default for State { fn default() -> Self { Self::new() } @@ -436,7 +435,7 @@ pub fn update<'a, T, Message>( on_selected: &dyn Fn(T) -> Message, selected: Option<&T>, options: &[T], - state: impl FnOnce() -> &'a mut State, + state: impl FnOnce() -> &'a mut State, ) -> event::Status where T: PartialEq + Clone + 'a, @@ -446,7 +445,7 @@ where | Event::Touch(touch::Event::FingerPressed { .. }) => { let state = state(); - let event_status = if state.is_open { + if state.is_open { // Event wasn't processed by overlay, so cursor was clicked either outside it's // bounds or on the drop-down, either way we close the overlay. state.is_open = false; @@ -460,16 +459,6 @@ where event::Status::Captured } else { event::Status::Ignored - }; - - if let Some(last_selection) = state.last_selection.take() { - shell.publish((on_selected)(last_selection)); - - state.is_open = false; - - event::Status::Captured - } else { - event_status } } Event::Mouse(mouse::Event::WheelScrolled { @@ -544,12 +533,13 @@ pub fn mouse_interaction( /// Returns the current overlay of a [`PickList`]. pub fn overlay<'a, T, Message, Renderer>( layout: Layout<'_>, - state: &'a mut State, + state: &'a mut State, padding: Padding, text_size: Option, text_shaping: text::Shaping, font: Renderer::Font, options: &'a [T], + on_selected: &'a dyn Fn(T) -> Message, style: ::Style, ) -> Option> where @@ -570,7 +560,11 @@ where &mut state.menu, options, &mut state.hovered_option, - &mut state.last_selection, + |option| { + state.is_open = false; + + (on_selected)(option) + }, ) .width(bounds.width) .padding(padding) @@ -603,7 +597,7 @@ pub fn draw<'a, T, Renderer>( selected: Option<&T>, handle: &Handle, style: &::Style, - state: impl FnOnce() -> &'a State, + state: impl FnOnce() -> &'a State, ) where Renderer: text::Renderer, Renderer::Theme: StyleSheet, -- cgit From 9803b276ad087846dfc3bb349113c5799ce00141 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 14 Jun 2023 11:27:42 +0200 Subject: Fix cursor availability in `overlay::Nested::draw` --- runtime/src/overlay/nested.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/overlay/nested.rs b/runtime/src/overlay/nested.rs index 5c5fafde..cd258ab6 100644 --- a/runtime/src/overlay/nested.rs +++ b/runtime/src/overlay/nested.rs @@ -94,7 +94,7 @@ where .and_then(|(cursor_position, nested_layout)| { element.overlay(layout, renderer).map(|nested| { nested.is_over( - nested_layout, + nested_layout.children().next().unwrap(), renderer, cursor_position, ) -- cgit