summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--examples/cached/Cargo.toml10
-rw-r--r--examples/cached/src/main.rs139
-rw-r--r--examples/lazy/src/main.rs162
-rw-r--r--lazy/src/lazy.rs120
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,
{