diff options
author | 2023-01-11 18:55:48 +0100 | |
---|---|---|
committer | 2023-01-11 18:55:48 +0100 | |
commit | ca337b880f82b3da264ac1626f973f8be29e95f4 (patch) | |
tree | 1dbf59aab15130724cd6453da6cd87afe948db49 | |
parent | 7ccd87c36b54e0d53f65f5774f140a0528ae4504 (diff) | |
parent | c7c5611087c37ab7bffcfcd91e19dbf71ccc8e11 (diff) | |
download | iced-ca337b880f82b3da264ac1626f973f8be29e95f4.tar.gz iced-ca337b880f82b3da264ac1626f973f8be29e95f4.tar.bz2 iced-ca337b880f82b3da264ac1626f973f8be29e95f4.zip |
Merge pull request #1644 from nicksenger/fix/lazy-widget
Fix `overlay` implementation for `Lazy` widget
Diffstat (limited to '')
-rw-r--r-- | examples/cached/Cargo.toml | 10 | ||||
-rw-r--r-- | examples/cached/src/main.rs | 139 | ||||
-rw-r--r-- | examples/lazy/src/main.rs | 162 | ||||
-rw-r--r-- | lazy/src/lazy.rs | 120 |
4 files changed, 212 insertions, 219 deletions
diff --git a/examples/cached/Cargo.toml b/examples/cached/Cargo.toml deleted file mode 100644 index 2c7edde2..00000000 --- a/examples/cached/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "cached" -version = "0.1.0" -authors = ["Nick Senger <dev@nsenger.com>"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../..", features = ["debug"] } -iced_lazy = { path = "../../lazy" } diff --git a/examples/cached/src/main.rs b/examples/cached/src/main.rs deleted file mode 100644 index 8845b874..00000000 --- a/examples/cached/src/main.rs +++ /dev/null @@ -1,139 +0,0 @@ -use iced::theme; -use iced::widget::{ - button, column, horizontal_space, row, scrollable, text, text_input, -}; -use iced::{Element, Length, Sandbox, Settings}; -use iced_lazy::lazy; - -use std::collections::HashSet; - -pub fn main() -> iced::Result { - App::run(Settings::default()) -} - -struct App { - options: HashSet<String>, - input: String, - order: Order, -} - -impl Default for App { - fn default() -> Self { - Self { - options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"] - .into_iter() - .map(ToString::to_string) - .collect(), - input: Default::default(), - order: Order::Ascending, - } - } -} - -#[derive(Debug, Clone)] -enum Message { - InputChanged(String), - ToggleOrder, - DeleteOption(String), - AddOption(String), -} - -impl Sandbox for App { - type Message = Message; - - fn new() -> Self { - Self::default() - } - - fn title(&self) -> String { - String::from("Cached - Iced") - } - - fn update(&mut self, message: Message) { - match message { - Message::InputChanged(input) => { - self.input = input; - } - Message::ToggleOrder => { - self.order = match self.order { - Order::Ascending => Order::Descending, - Order::Descending => Order::Ascending, - } - } - Message::AddOption(option) => { - self.options.insert(option); - self.input.clear(); - } - Message::DeleteOption(option) => { - self.options.remove(&option); - } - } - } - - fn view(&self) -> Element<Message> { - let options = lazy((&self.order, self.options.len()), || { - let mut options: Vec<_> = self.options.iter().collect(); - - options.sort_by(|a, b| match self.order { - Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()), - Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()), - }); - - column( - options - .into_iter() - .map(|option| { - row![ - text(option), - horizontal_space(Length::Fill), - button("Delete") - .on_press(Message::DeleteOption( - option.to_string(), - ),) - .style(theme::Button::Destructive) - ] - .into() - }) - .collect(), - ) - .spacing(10) - }); - - column![ - scrollable(options).height(Length::Fill), - row![ - text_input( - "Add a new option", - &self.input, - Message::InputChanged, - ) - .on_submit(Message::AddOption(self.input.clone())), - button(text(format!("Toggle Order ({})", self.order))) - .on_press(Message::ToggleOrder) - ] - .spacing(10) - ] - .spacing(20) - .padding(20) - .into() - } -} - -#[derive(Debug, Hash)] -enum Order { - Ascending, - Descending, -} - -impl std::fmt::Display for Order { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Ascending => "Ascending", - Self::Descending => "Descending", - } - ) - } -} diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 8845b874..818c91bc 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -1,18 +1,21 @@ use iced::theme; use iced::widget::{ - button, column, horizontal_space, row, scrollable, text, text_input, + button, column, horizontal_space, pick_list, row, scrollable, text, + text_input, }; use iced::{Element, Length, Sandbox, Settings}; use iced_lazy::lazy; use std::collections::HashSet; +use std::hash::Hash; pub fn main() -> iced::Result { App::run(Settings::default()) } struct App { - options: HashSet<String>, + version: u8, + items: HashSet<Item>, input: String, order: Order, } @@ -20,9 +23,10 @@ struct App { impl Default for App { fn default() -> Self { Self { - options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"] + version: 0, + items: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"] .into_iter() - .map(ToString::to_string) + .map(From::from) .collect(), input: Default::default(), order: Order::Ascending, @@ -30,12 +34,92 @@ impl Default for App { } } +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +enum Color { + #[default] + Black, + Red, + Orange, + Yellow, + Green, + Blue, + Purple, +} + +impl Color { + const ALL: &[Color] = &[ + Color::Black, + Color::Red, + Color::Orange, + Color::Yellow, + Color::Green, + Color::Blue, + Color::Purple, + ]; +} + +impl std::fmt::Display for Color { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Black => "Black", + Self::Red => "Red", + Self::Orange => "Orange", + Self::Yellow => "Yellow", + Self::Green => "Green", + Self::Blue => "Blue", + Self::Purple => "Purple", + }) + } +} + +impl From<Color> for iced::Color { + fn from(value: Color) -> Self { + match value { + Color::Black => iced::Color::from_rgb8(0, 0, 0), + Color::Red => iced::Color::from_rgb8(220, 50, 47), + Color::Orange => iced::Color::from_rgb8(203, 75, 22), + Color::Yellow => iced::Color::from_rgb8(181, 137, 0), + Color::Green => iced::Color::from_rgb8(133, 153, 0), + Color::Blue => iced::Color::from_rgb8(38, 139, 210), + Color::Purple => iced::Color::from_rgb8(108, 113, 196), + } + } +} + +#[derive(Clone, Debug, Eq)] +struct Item { + name: String, + color: Color, +} + +impl Hash for Item { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl PartialEq for Item { + fn eq(&self, other: &Self) -> bool { + self.name.eq(&other.name) + } +} + +impl From<&str> for Item { + fn from(s: &str) -> Self { + Self { + name: s.to_owned(), + color: Default::default(), + } + } +} + #[derive(Debug, Clone)] enum Message { InputChanged(String), ToggleOrder, - DeleteOption(String), - AddOption(String), + DeleteItem(Item), + AddItem(String), + ItemColorChanged(Item, Color), } impl Sandbox for App { @@ -46,7 +130,7 @@ impl Sandbox for App { } fn title(&self) -> String { - String::from("Cached - Iced") + String::from("Lazy - Iced") } fn update(&mut self, message: Message) { @@ -55,43 +139,71 @@ impl Sandbox for App { self.input = input; } Message::ToggleOrder => { + self.version = self.version.wrapping_add(1); self.order = match self.order { Order::Ascending => Order::Descending, Order::Descending => Order::Ascending, } } - Message::AddOption(option) => { - self.options.insert(option); + Message::AddItem(name) => { + self.version = self.version.wrapping_add(1); + self.items.insert(name.as_str().into()); self.input.clear(); } - Message::DeleteOption(option) => { - self.options.remove(&option); + Message::DeleteItem(item) => { + self.version = self.version.wrapping_add(1); + self.items.remove(&item); + } + Message::ItemColorChanged(item, color) => { + self.version = self.version.wrapping_add(1); + if self.items.remove(&item) { + self.items.insert(Item { + name: item.name, + color, + }); + } } } } fn view(&self) -> Element<Message> { - let options = lazy((&self.order, self.options.len()), || { - let mut options: Vec<_> = self.options.iter().collect(); + let options = lazy(self.version, || { + let mut items: Vec<_> = self.items.iter().cloned().collect(); - options.sort_by(|a, b| match self.order { - Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()), - Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()), + items.sort_by(|a, b| match self.order { + Order::Ascending => { + a.name.to_lowercase().cmp(&b.name.to_lowercase()) + } + Order::Descending => { + b.name.to_lowercase().cmp(&a.name.to_lowercase()) + } }); column( - options + items .into_iter() - .map(|option| { + .map(|item| { + let button = button("Delete") + .on_press(Message::DeleteItem(item.clone())) + .style(theme::Button::Destructive); + row![ - text(option), + text(&item.name) + .style(theme::Text::Color(item.color.into())), horizontal_space(Length::Fill), - button("Delete") - .on_press(Message::DeleteOption( - option.to_string(), - ),) - .style(theme::Button::Destructive) + pick_list( + Color::ALL, + Some(item.color), + move |color| { + Message::ItemColorChanged( + item.clone(), + color, + ) + } + ), + button ] + .spacing(20) .into() }) .collect(), @@ -107,7 +219,7 @@ impl Sandbox for App { &self.input, Message::InputChanged, ) - .on_submit(Message::AddOption(self.input.clone())), + .on_submit(Message::AddItem(self.input.clone())), button(text(format!("Toggle Order ({})", self.order))) .on_press(Message::ToggleOrder) ] diff --git a/lazy/src/lazy.rs b/lazy/src/lazy.rs index ec35e8f0..16709f8d 100644 --- a/lazy/src/lazy.rs +++ b/lazy/src/lazy.rs @@ -9,16 +9,17 @@ use iced_native::Element; use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell, Size}; use ouroboros::self_referencing; -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::RefCell; use std::hash::{Hash, Hasher as H}; -use std::marker::PhantomData; use std::rc::Rc; #[allow(missing_debug_implementations)] pub struct Lazy<'a, Message, Renderer, Dependency, View> { dependency: Dependency, view: Box<dyn Fn() -> View + 'a>, - element: RefCell<Option<Rc<RefCell<Element<'static, Message, Renderer>>>>>, + element: RefCell< + Option<Rc<RefCell<Option<Element<'static, Message, Renderer>>>>>, + >, } impl<'a, Message, Renderer, Dependency, View> @@ -37,21 +38,35 @@ where fn with_element<T>( &self, - f: impl FnOnce(Ref<Element<Message, Renderer>>) -> T, + f: impl FnOnce(&Element<Message, Renderer>) -> T, ) -> T { - f(self.element.borrow().as_ref().unwrap().borrow()) + f(self + .element + .borrow() + .as_ref() + .unwrap() + .borrow() + .as_ref() + .unwrap()) } fn with_element_mut<T>( &self, - f: impl FnOnce(RefMut<Element<Message, Renderer>>) -> T, + f: impl FnOnce(&mut Element<Message, Renderer>) -> T, ) -> T { - f(self.element.borrow().as_ref().unwrap().borrow_mut()) + f(self + .element + .borrow() + .as_ref() + .unwrap() + .borrow_mut() + .as_mut() + .unwrap()) } } struct Internal<Message, Renderer> { - element: Rc<RefCell<Element<'static, Message, Renderer>>>, + element: Rc<RefCell<Option<Element<'static, Message, Renderer>>>>, hash: u64, } @@ -73,7 +88,7 @@ where self.dependency.hash(&mut hasher); let hash = hasher.finish(); - let element = Rc::new(RefCell::new((self.view)().into())); + let element = Rc::new(RefCell::new(Some((self.view)().into()))); (*self.element.borrow_mut()) = Some(element.clone()); @@ -81,9 +96,7 @@ where } fn children(&self) -> Vec<Tree> { - vec![Tree::new( - self.element.borrow().as_ref().unwrap().borrow().as_widget(), - )] + self.with_element(|element| vec![Tree::new(element.as_widget())]) } fn diff(&self, tree: &mut Tree) { @@ -97,12 +110,12 @@ where current.hash = new_hash; let element = (self.view)().into(); - current.element = Rc::new(RefCell::new(element)); + current.element = Rc::new(RefCell::new(Some(element))); (*self.element.borrow_mut()) = Some(current.element.clone()); - tree.diff_children(std::slice::from_ref( - &self.element.borrow().as_ref().unwrap().borrow().as_widget(), - )); + self.with_element(|element| { + tree.diff_children(std::slice::from_ref(&element.as_widget())) + }); } else { (*self.element.borrow_mut()) = Some(current.element.clone()); } @@ -153,7 +166,7 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { - self.with_element_mut(|mut element| { + self.with_element_mut(|element| { element.as_widget_mut().on_event( &mut tree.children[0], event, @@ -214,23 +227,27 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option<overlay::Element<'_, Message, Renderer>> { - let overlay = OverlayBuilder { - cached: self, - tree: &mut tree.children[0], - types: PhantomData, - overlay_builder: |cached, tree| { - Rc::get_mut(cached.element.get_mut().as_mut().unwrap()) + let overlay = Overlay(Some( + InnerBuilder { + cell: self.element.borrow().as_ref().unwrap().clone(), + element: self + .element + .borrow() + .as_ref() .unwrap() - .get_mut() - .as_widget_mut() - .overlay(tree, layout, renderer) - }, - } - .build(); - - let has_overlay = overlay.with_overlay(|overlay| { - overlay.as_ref().map(overlay::Element::position) - }); + .borrow_mut() + .take() + .unwrap(), + tree: &mut tree.children[0], + overlay_builder: |element, tree| { + element.as_widget_mut().overlay(tree, layout, renderer) + }, + } + .build(), + )); + + let has_overlay = overlay + .with_overlay_maybe(|overlay| overlay::Element::position(overlay)); has_overlay .map(|position| overlay::Element::new(position, Box::new(overlay))) @@ -238,37 +255,50 @@ where } #[self_referencing] -struct Overlay<'a, 'b, Message, Renderer, Dependency, View> { - cached: &'a mut Lazy<'b, Message, Renderer, Dependency, View>, +struct Inner<'a, Message, Renderer> +where + Message: 'a, + Renderer: 'a, +{ + cell: Rc<RefCell<Option<Element<'static, Message, Renderer>>>>, + element: Element<'static, Message, Renderer>, tree: &'a mut Tree, - types: PhantomData<(Message, Dependency, View)>, - #[borrows(mut cached, mut tree)] + #[borrows(mut element, mut tree)] #[covariant] overlay: Option<overlay::Element<'this, Message, Renderer>>, } -impl<'a, 'b, Message, Renderer, Dependency, View> - Overlay<'a, 'b, Message, Renderer, Dependency, View> -{ +struct Overlay<'a, Message, Renderer>(Option<Inner<'a, Message, Renderer>>); + +impl<'a, Message, Renderer> Drop for Overlay<'a, Message, Renderer> { + fn drop(&mut self) { + let heads = self.0.take().unwrap().into_heads(); + (*heads.cell.borrow_mut()) = Some(heads.element); + } +} + +impl<'a, Message, Renderer> Overlay<'a, Message, Renderer> { fn with_overlay_maybe<T>( &self, f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, ) -> Option<T> { - self.borrow_overlay().as_ref().map(f) + self.0.as_ref().unwrap().borrow_overlay().as_ref().map(f) } fn with_overlay_mut_maybe<T>( &mut self, f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, ) -> Option<T> { - self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) + self.0 + .as_mut() + .unwrap() + .with_overlay_mut(|overlay| overlay.as_mut().map(f)) } } -impl<'a, 'b, Message, Renderer, Dependency, View> - overlay::Overlay<Message, Renderer> - for Overlay<'a, 'b, Message, Renderer, Dependency, View> +impl<'a, Message, Renderer> overlay::Overlay<Message, Renderer> + for Overlay<'a, Message, Renderer> where Renderer: iced_native::Renderer, { |