From 413e6cac288fd4ceaec88e09373d44875803398b Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 15:28:18 -0500 Subject: [PATCH 01/16] adding ListIter implementations and examples --- druid/Cargo.toml | 4 + druid/examples/list_variants.rs | 215 ++++++++++++++++++++++++++++++++ druid/src/widget/list.rs | 124 +++++++++++++++++- 3 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 druid/examples/list_variants.rs diff --git a/druid/Cargo.toml b/druid/Cargo.toml index c7131f9f85..b4ff479470 100644 --- a/druid/Cargo.toml +++ b/druid/Cargo.toml @@ -97,6 +97,10 @@ required-features = ["image", "png"] name = "invalidation" required-features = ["im"] +[[example]] +name = "list_variants" +required-features = ["im"] + [[example]] name = "list" required-features = ["im"] diff --git a/druid/examples/list_variants.rs b/druid/examples/list_variants.rs new file mode 100644 index 0000000000..b317ab701c --- /dev/null +++ b/druid/examples/list_variants.rs @@ -0,0 +1,215 @@ +// Copyright 2019 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Demos alternative iterable data types for the List widget. + +use druid::im::{hashmap, ordmap, vector, HashMap, OrdMap, Vector}; +use druid::lens::{self, LensExt}; +use druid::widget::{Button, CrossAxisAlignment, Flex, Label, List, Scroll}; +use druid::{ + AppLauncher, Color, Data, Lens, LocalizedString, UnitPoint, Widget, WidgetExt, WindowDesc, +}; + +#[derive(Clone, Data, Lens)] +struct AppData { + hm_values: HashMap, + hm_keys_values: HashMap, + om_values: OrdMap, + om_keys_values: OrdMap, + hm_values_index: usize, + hm_keys_values_index: usize, + om_values_index: usize, + om_keys_values_index: usize, +} + +pub fn main() { + let main_window = WindowDesc::new(ui_builder()) + .window_size((1200.0, 600.0)) + .title( + LocalizedString::new("list-variants-demo-window-title") + .with_placeholder("List Variants Demo"), + ); + // Set our initial data + let hm_values = hashmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; + let hm_keys_values = hashmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, + String::from("China") => 956960}; + let om_values = ordmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; + let om_keys_values = ordmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, + String::from("China") => 956960}; + let data = AppData { + hm_values_index: hm_values.len(), + hm_keys_values_index: hm_keys_values.len(), + om_values_index: om_values.len(), + om_keys_values_index: om_keys_values.len(), + hm_values, + hm_keys_values, + om_values, + om_keys_values, + }; + AppLauncher::with_window(main_window) + .use_env_tracing() + .launch(data) + .expect("launch failed"); +} + +fn ui_builder() -> impl Widget { + let mut root = Flex::column(); + + // Build a button to add children to both lists + // root.add_child( + // Button::new("Add") + // .on_click(|_, data: &mut AppData, _| { + // // Add child to left list + // data.l_index += 1; + // data.hm_values + // .insert(data.l_index as u32, String::from("Another Fruit")); + + // // Add child to right list + // data.r_index += 1; + // data.right.push_back(data.r_index as u32); + // }) + // .fix_height(30.0) + // .expand_width(), + // ); + + let mut lists = Flex::row().cross_axis_alignment(CrossAxisAlignment::Start); + + // Build a list of values from a hashmap + // The display order will be indeterminate. + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::HashMap Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &String, _env: &_| format!("{}", item)) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.5, 0.5)) + })) + .vertical() + .lens(AppData::hm_values), + ), + 1.0, + ); + + // Build a list of key value pairs from a hashmap + // The display order will be indeterminate. + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::HashMap Keys and Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &(String, u64), _env: &_| { + format!("{0}: {1} square kilometres", item.0, item.1) + }) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.0, 0.5)) + })) + .vertical() + .lens(AppData::hm_keys_values), + ), + 1.0, + ); + + // Build a list values from an ordmap + // The display order will be based on the Ord trait of the keys + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::OrdMap Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &String, _env: &_| format!("{}", item)) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.5, 0.5)) + })) + .vertical() + .lens(AppData::om_values), + ), + 1.0, + ); + + // Build a list of key value pairs from an ordmap + // The display order will be based on the Ord trait of the keys + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::OrdMap Keys and Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &(String, u64), _env: &_| { + format!("{0}: {1} square kilometres", item.0, item.1) + }) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.0, 0.5)) + })) + .vertical() + .lens(AppData::om_keys_values), + ), + 1.0, + ); + + // // Build a list with shared data + // lists.add_flex_child( + // Scroll::new( + // List::new(|| { + // Flex::row() + // .with_child( + // Label::new(|(_, item): &(Vector, u32), _env: &_| { + // format!("List item #{}", item) + // }) + // .align_vertical(UnitPoint::LEFT), + // ) + // .with_flex_spacer(1.0) + // .with_child( + // Button::new("Delete") + // .on_click(|_ctx, (shared, item): &mut (Vector, u32), _env| { + // // We have access to both child's data and shared data. + // // Remove element from right list. + // shared.retain(|v| v != item); + // }) + // .fix_size(80.0, 20.0) + // .align_vertical(UnitPoint::CENTER), + // ) + // .padding(10.0) + // .background(Color::rgb(0.5, 0.0, 0.5)) + // .fix_height(50.0) + // }) + // .with_spacing(10.), + // ) + // .vertical() + // .lens(lens::Identity.map( + // // Expose shared data with children data + // |d: &AppData| (d.right.clone(), d.right.clone()), + // |d: &mut AppData, x: (Vector, Vector)| { + // // If shared data was changed reflect the changes in our AppData + // d.right = x.0 + // }, + // )), + // 1.0, + // ); + + root.add_flex_child(lists, 1.0); + + root +} diff --git a/druid/src/widget/list.rs b/druid/src/widget/list.rs index 5d43a73794..788d9fc52a 100644 --- a/druid/src/widget/list.rs +++ b/druid/src/widget/list.rs @@ -19,7 +19,7 @@ use std::f64; use std::sync::Arc; #[cfg(feature = "im")] -use crate::im::Vector; +use crate::im::{HashMap, OrdMap, Vector}; use crate::kurbo::{Point, Rect, Size}; @@ -116,6 +116,128 @@ impl ListIter for Vector { } } +#[cfg(feature = "im")] +//We sidestep dealing with ListIter<(K, V)> here. +//That would require us to deal with changing keys and key collisions. +impl ListIter for HashMap +where + K: Data + std::hash::Hash + std::cmp::Eq, + V: Data, +{ + fn for_each(&self, mut cb: impl FnMut(&V, usize)) { + for (i, item) in self.iter().enumerate() { + let ret = (item.0.to_owned(), item.1.to_owned()); + cb(&ret.1, i); + } + } + + fn for_each_mut(&mut self, mut cb: impl FnMut(&mut V, usize)) { + for (i, item) in self.iter_mut().enumerate() { + let mut ret = (item.0.clone(), item.1.clone()); + cb(&mut ret.1, i); + + if !item.1.same(&ret.1) { + *item.1 = ret.1; + } + } + } + + fn data_len(&self) -> usize { + self.len() + } +} + +#[cfg(feature = "im")] +//TODO: This implementation just disregards any changes to the key. It makes it visible, but nothing else. +impl ListIter<(K, V)> for HashMap +where + K: Data + std::hash::Hash + std::cmp::Eq, + V: Data, +{ + fn for_each(&self, mut cb: impl FnMut(&(K, V), usize)) { + for (i, item) in self.iter().enumerate() { + let ret = (item.0.to_owned(), item.1.to_owned()); + cb(&ret, i); + } + } + + fn for_each_mut(&mut self, mut cb: impl FnMut(&mut (K, V), usize)) { + for (i, item) in self.iter_mut().enumerate() { + let mut ret = (item.0.clone(), item.1.clone()); + cb(&mut ret, i); + + if !item.1.same(&ret.1) { + *item.1 = ret.1; + } + } + } + + fn data_len(&self) -> usize { + self.len() + } +} + +#[cfg(feature = "im")] +//We sidestep dealing with ListIter<(K, V)> here. +//That would require us to deal with changing keys and key collisions. +impl ListIter for OrdMap +where + K: Data + Ord, + V: Data, +{ + fn for_each(&self, mut cb: impl FnMut(&V, usize)) { + for (i, item) in self.iter().enumerate() { + let ret = (item.0.to_owned(), item.1.to_owned()); + cb(&ret.1, i); + } + } + + fn for_each_mut(&mut self, mut cb: impl FnMut(&mut V, usize)) { + for (i, item) in self.clone().iter().enumerate() { + let mut ret = (item.0.clone(), item.1.clone()); + cb(&mut ret.1, i); + + if !item.1.same(&ret.1) { + self[&item.0] = ret.1; + } + } + } + + fn data_len(&self) -> usize { + self.len() + } +} + +#[cfg(feature = "im")] +//TODO: This implementation just disregards any changes to the key. It makes it visible, but nothing else. +impl ListIter<(K, V)> for OrdMap +where + K: Data + Ord, + V: Data, +{ + fn for_each(&self, mut cb: impl FnMut(&(K, V), usize)) { + for (i, item) in self.iter().enumerate() { + let ret = (item.0.to_owned(), item.1.to_owned()); + cb(&ret, i); + } + } + + fn for_each_mut(&mut self, mut cb: impl FnMut(&mut (K, V), usize)) { + for (i, item) in self.clone().iter().enumerate() { + let mut ret = (item.0.clone(), item.1.clone()); + cb(&mut ret, i); + + if !item.1.same(&ret.1) { + self[&ret.0] = ret.1; + } + } + } + + fn data_len(&self) -> usize { + self.len() + } +} + // S == shared data type #[cfg(feature = "im")] impl ListIter<(S, T)> for (S, Vector) { From 520202cd3150053c04718263ef91ab09dbe87a7f Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 17:50:22 -0500 Subject: [PATCH 02/16] Changed example name, added modifying via buttons. --- druid/Cargo.toml | 2 +- druid/examples/list_sources.rs | 213 +++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 druid/examples/list_sources.rs diff --git a/druid/Cargo.toml b/druid/Cargo.toml index b4ff479470..3c3f54ea06 100644 --- a/druid/Cargo.toml +++ b/druid/Cargo.toml @@ -98,7 +98,7 @@ name = "invalidation" required-features = ["im"] [[example]] -name = "list_variants" +name = "list_sources" required-features = ["im"] [[example]] diff --git a/druid/examples/list_sources.rs b/druid/examples/list_sources.rs new file mode 100644 index 0000000000..53c37baa43 --- /dev/null +++ b/druid/examples/list_sources.rs @@ -0,0 +1,213 @@ +// Copyright 2019 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Demos alternative iterable data types for the List widget, im::HashMap and im::OrdMap + +use druid::im::{hashmap, ordmap, vector, HashMap, OrdMap, Vector}; +use druid::lens::{self, LensExt}; +use druid::widget::{Button, CrossAxisAlignment, Flex, Label, List, Scroll}; +use druid::{ + AppLauncher, Color, Data, Lens, LocalizedString, UnitPoint, Widget, WidgetExt, WindowDesc, +}; + +#[derive(Clone, Data, Lens)] +struct AppData { + adding_index: usize, + hm_values: HashMap, + hm_keys_values: HashMap, + om_values: OrdMap, + om_keys_values: OrdMap, +} + +pub fn main() { + let main_window = WindowDesc::new(ui_builder()) + .window_size((1200.0, 600.0)) + .title( + LocalizedString::new("list-sources-demo-window-title") + .with_placeholder("List Sources Demo"), + ); + // Set our initial data. The HashMap and OrdMap types will behave differently ordering the same data. + let hm_values = hashmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; + let hm_keys_values = hashmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, + String::from("China") => 956960}; + let om_values = ordmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; + let om_keys_values = ordmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, + String::from("China") => 956960}; + let data = AppData { + adding_index: hm_values.len(), + hm_values, + hm_keys_values, + om_values, + om_keys_values, + }; + AppLauncher::with_window(main_window) + .use_env_tracing() + .launch(data) + .expect("launch failed"); +} + +fn ui_builder() -> impl Widget { + let mut root = Flex::column(); + + //Buttons to test adding and removing entries from the lists + root.add_child( + Button::new("Add") + .on_click(|_, data: &mut AppData, _| { + data.adding_index += 1; + data.hm_values.insert( + data.adding_index as u32, + String::from(format!("Fruit #{}", data.adding_index)), + ); + data.hm_keys_values + .insert(String::from(format!("Country #{}", data.adding_index)), 42); + data.om_values.insert( + data.adding_index as u32, + String::from(format!("Fruit #{}", data.adding_index)), + ); + data.om_keys_values + .insert(String::from(format!("Country #{}", data.adding_index)), 42); + }) + .fix_height(30.0) + .expand_width(), + ); + root.add_child( + Button::new("Remove First") + .on_click(|_, data: &mut AppData, _| { + if data.hm_values.len() > 0 { + match data.hm_values.clone().iter().next() { + Some(k) => { + data.hm_values.remove(&k.0.clone()); + } + _ => (), + } + } + if data.hm_keys_values.len() > 0 { + match data.hm_keys_values.clone().iter().next() { + Some(k) => { + data.hm_keys_values.remove(&k.0.clone()); + } + _ => (), + } + } + if data.om_values.len() > 0 { + match data.om_values.clone().iter().next() { + Some(k) => { + data.om_values.remove(&k.0.clone()); + } + _ => (), + } + } + if data.om_keys_values.len() > 0 { + match data.om_keys_values.clone().iter().next() { + Some(k) => { + data.om_keys_values.remove(&k.0.clone()); + } + _ => (), + } + } + }) + .fix_height(30.0) + .expand_width(), + ); + + let mut lists = Flex::row().cross_axis_alignment(CrossAxisAlignment::Start); + + // Build a list of values from a hashmap + // The display order will be indeterminate. + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::HashMap Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &String, _env: &_| format!("{}", item)) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.5, 0.5)) + })) + .vertical() + .lens(AppData::hm_values), + ), + 1.0, + ); + + // Build a list of key value pairs from a hashmap + // The display order will be indeterminate. + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::HashMap Keys and Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &(String, u64), _env: &_| { + format!("{0}: {1} square kilometres", item.0, item.1) + }) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.0, 0.5)) + })) + .vertical() + .lens(AppData::hm_keys_values), + ), + 1.0, + ); + + // Build a list values from an ordmap + // The display order will be based on the Ord trait of the keys + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::OrdMap Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &String, _env: &_| format!("{}", item)) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.5, 0.5)) + })) + .vertical() + .lens(AppData::om_values), + ), + 1.0, + ); + + // Build a list of key value pairs from an ordmap + // The display order will be based on the Ord trait of the keys + lists.add_flex_child( + Flex::column() + .with_child(Label::new("List from im::OrdMap Keys and Values")) + .with_child( + Scroll::new(List::new(|| { + Label::new(|item: &(String, u64), _env: &_| { + format!("{0}: {1} square kilometres", item.0, item.1) + }) + .align_vertical(UnitPoint::LEFT) + .padding(10.0) + .expand() + .height(50.0) + .background(Color::rgb(0.5, 0.0, 0.5)) + })) + .vertical() + .lens(AppData::om_keys_values), + ), + 1.0, + ); + + root.add_flex_child(lists, 1.0); + + root +} From 96710329465916e113bea4f3afed8db7e5245de5 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 17:51:00 -0500 Subject: [PATCH 03/16] handling changes of Key for OrdMap and HashMap --- druid/examples/list_variants.rs | 215 -------------------------------- druid/src/widget/list.rs | 14 ++- 2 files changed, 11 insertions(+), 218 deletions(-) delete mode 100644 druid/examples/list_variants.rs diff --git a/druid/examples/list_variants.rs b/druid/examples/list_variants.rs deleted file mode 100644 index b317ab701c..0000000000 --- a/druid/examples/list_variants.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2019 The Druid Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Demos alternative iterable data types for the List widget. - -use druid::im::{hashmap, ordmap, vector, HashMap, OrdMap, Vector}; -use druid::lens::{self, LensExt}; -use druid::widget::{Button, CrossAxisAlignment, Flex, Label, List, Scroll}; -use druid::{ - AppLauncher, Color, Data, Lens, LocalizedString, UnitPoint, Widget, WidgetExt, WindowDesc, -}; - -#[derive(Clone, Data, Lens)] -struct AppData { - hm_values: HashMap, - hm_keys_values: HashMap, - om_values: OrdMap, - om_keys_values: OrdMap, - hm_values_index: usize, - hm_keys_values_index: usize, - om_values_index: usize, - om_keys_values_index: usize, -} - -pub fn main() { - let main_window = WindowDesc::new(ui_builder()) - .window_size((1200.0, 600.0)) - .title( - LocalizedString::new("list-variants-demo-window-title") - .with_placeholder("List Variants Demo"), - ); - // Set our initial data - let hm_values = hashmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; - let hm_keys_values = hashmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, - String::from("China") => 956960}; - let om_values = ordmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; - let om_keys_values = ordmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, - String::from("China") => 956960}; - let data = AppData { - hm_values_index: hm_values.len(), - hm_keys_values_index: hm_keys_values.len(), - om_values_index: om_values.len(), - om_keys_values_index: om_keys_values.len(), - hm_values, - hm_keys_values, - om_values, - om_keys_values, - }; - AppLauncher::with_window(main_window) - .use_env_tracing() - .launch(data) - .expect("launch failed"); -} - -fn ui_builder() -> impl Widget { - let mut root = Flex::column(); - - // Build a button to add children to both lists - // root.add_child( - // Button::new("Add") - // .on_click(|_, data: &mut AppData, _| { - // // Add child to left list - // data.l_index += 1; - // data.hm_values - // .insert(data.l_index as u32, String::from("Another Fruit")); - - // // Add child to right list - // data.r_index += 1; - // data.right.push_back(data.r_index as u32); - // }) - // .fix_height(30.0) - // .expand_width(), - // ); - - let mut lists = Flex::row().cross_axis_alignment(CrossAxisAlignment::Start); - - // Build a list of values from a hashmap - // The display order will be indeterminate. - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::HashMap Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &String, _env: &_| format!("{}", item)) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.5, 0.5)) - })) - .vertical() - .lens(AppData::hm_values), - ), - 1.0, - ); - - // Build a list of key value pairs from a hashmap - // The display order will be indeterminate. - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::HashMap Keys and Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &(String, u64), _env: &_| { - format!("{0}: {1} square kilometres", item.0, item.1) - }) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.0, 0.5)) - })) - .vertical() - .lens(AppData::hm_keys_values), - ), - 1.0, - ); - - // Build a list values from an ordmap - // The display order will be based on the Ord trait of the keys - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::OrdMap Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &String, _env: &_| format!("{}", item)) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.5, 0.5)) - })) - .vertical() - .lens(AppData::om_values), - ), - 1.0, - ); - - // Build a list of key value pairs from an ordmap - // The display order will be based on the Ord trait of the keys - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::OrdMap Keys and Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &(String, u64), _env: &_| { - format!("{0}: {1} square kilometres", item.0, item.1) - }) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.0, 0.5)) - })) - .vertical() - .lens(AppData::om_keys_values), - ), - 1.0, - ); - - // // Build a list with shared data - // lists.add_flex_child( - // Scroll::new( - // List::new(|| { - // Flex::row() - // .with_child( - // Label::new(|(_, item): &(Vector, u32), _env: &_| { - // format!("List item #{}", item) - // }) - // .align_vertical(UnitPoint::LEFT), - // ) - // .with_flex_spacer(1.0) - // .with_child( - // Button::new("Delete") - // .on_click(|_ctx, (shared, item): &mut (Vector, u32), _env| { - // // We have access to both child's data and shared data. - // // Remove element from right list. - // shared.retain(|v| v != item); - // }) - // .fix_size(80.0, 20.0) - // .align_vertical(UnitPoint::CENTER), - // ) - // .padding(10.0) - // .background(Color::rgb(0.5, 0.0, 0.5)) - // .fix_height(50.0) - // }) - // .with_spacing(10.), - // ) - // .vertical() - // .lens(lens::Identity.map( - // // Expose shared data with children data - // |d: &AppData| (d.right.clone(), d.right.clone()), - // |d: &mut AppData, x: (Vector, Vector)| { - // // If shared data was changed reflect the changes in our AppData - // d.right = x.0 - // }, - // )), - // 1.0, - // ); - - root.add_flex_child(lists, 1.0); - - root -} diff --git a/druid/src/widget/list.rs b/druid/src/widget/list.rs index 788d9fc52a..1ea38a89b9 100644 --- a/druid/src/widget/list.rs +++ b/druid/src/widget/list.rs @@ -197,8 +197,12 @@ where let mut ret = (item.0.clone(), item.1.clone()); cb(&mut ret.1, i); - if !item.1.same(&ret.1) { - self[&item.0] = ret.1; + //If item.0(Key) is different, we remove and reinsert with the new key. + if !item.0.same(&ret.0) { + self.remove(&item.0); + self.insert(ret.0, ret.1); + } else if !item.1.same(&ret.1) { + self[&ret.0] = ret.1; } } } @@ -227,7 +231,11 @@ where let mut ret = (item.0.clone(), item.1.clone()); cb(&mut ret, i); - if !item.1.same(&ret.1) { + //If item.0(Key) is different, we remove and reinsert with the new key. + if !item.0.same(&ret.0) { + self.remove(&item.0); + self.insert(ret.0, ret.1); + } else if !item.1.same(&ret.1) { self[&ret.0] = ret.1; } } From 73e5bbaf9b44761af0cecc688e0d430cf33990fc Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 17:56:49 -0500 Subject: [PATCH 04/16] Added lejero user, pull request detail --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4d603c013..fdce7400d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ You can find its changes [documented below](#070---2021-01-01). - Implemented `Data` for more datatypes from `std` ([#1534] by [@derekdreery]) - Shell: windows implementation from content_insets ([#1592] by [@HoNile]) - Scroll::content_must_fill and a few other new Scroll methods ([#1635] by [@cmyr]) +- Added ListIter implementations for HashMap and OrdMap with list_sources example ([#1700] guess by [@lejero]) ### Changed @@ -423,6 +424,7 @@ Last release without a changelog :( [@Poignardazur]: https://github.com/PoignardAzur [@HoNile]: https://github.com/HoNile [@SecondFlight]: https://github.com/SecondFlight +[@Lejero]: https://githum.com/lejero [#599]: https://github.com/linebender/druid/pull/599 [#611]: https://github.com/linebender/druid/pull/611 From 99167ef00f338571dfd44745c23a0619d3587e23 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 17:59:00 -0500 Subject: [PATCH 05/16] changelog edit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdce7400d8..a86666c606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -424,7 +424,7 @@ Last release without a changelog :( [@Poignardazur]: https://github.com/PoignardAzur [@HoNile]: https://github.com/HoNile [@SecondFlight]: https://github.com/SecondFlight -[@Lejero]: https://githum.com/lejero +[@Lejero]: https://githum.com/Lejero [#599]: https://github.com/linebender/druid/pull/599 [#611]: https://github.com/linebender/druid/pull/611 From b52fe80f9606bbf9b24c2904d0845c2730d25d68 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 17:59:26 -0500 Subject: [PATCH 06/16] github spelling mistake --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a86666c606..ad22f9c077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -424,7 +424,7 @@ Last release without a changelog :( [@Poignardazur]: https://github.com/PoignardAzur [@HoNile]: https://github.com/HoNile [@SecondFlight]: https://github.com/SecondFlight -[@Lejero]: https://githum.com/Lejero +[@Lejero]: https://github.com/Lejero [#599]: https://github.com/linebender/druid/pull/599 [#611]: https://github.com/linebender/druid/pull/611 From 2c95175b30996f5c33c1f56da59ba63697e01541 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 18:10:45 -0500 Subject: [PATCH 07/16] change log overwrite --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad22f9c077..a5d18eb4d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,8 @@ You can find its changes [documented below](#070---2021-01-01). - WindowSizePolicy: allow windows to be sized by their content ([#1532] by [@rjwittams]) - Implemented `Data` for more datatypes from `std` ([#1534] by [@derekdreery]) - Shell: windows implementation from content_insets ([#1592] by [@HoNile]) +- Shell: IME API and macOS IME implementation ([#1619] by [@lord]) - Scroll::content_must_fill and a few other new Scroll methods ([#1635] by [@cmyr]) -- Added ListIter implementations for HashMap and OrdMap with list_sources example ([#1700] guess by [@lejero]) ### Changed @@ -29,6 +29,7 @@ You can find its changes [documented below](#070---2021-01-01). - `WindowDesc::new` takes the root widget directly instead of a closure ([#1559] by [@lassipulkkinen]) - Switch to trace-based logging ([#1562] by [@PoignardAzur]) - Spacers in `Flex` are now implemented by calculating the space in `Flex` instead of creating a widget for it ([#1584] by [@JAicewizard]) +- Padding is generic over child widget, impls WidgetWrapper ([#1634] by [@cmyr]) ### Deprecated @@ -424,7 +425,7 @@ Last release without a changelog :( [@Poignardazur]: https://github.com/PoignardAzur [@HoNile]: https://github.com/HoNile [@SecondFlight]: https://github.com/SecondFlight -[@Lejero]: https://github.com/Lejero +[@lord]: https://github.com/lord [#599]: https://github.com/linebender/druid/pull/599 [#611]: https://github.com/linebender/druid/pull/611 @@ -628,6 +629,8 @@ Last release without a changelog :( [#1596]: https://github.com/linebender/druid/pull/1596 [#1600]: https://github.com/linebender/druid/pull/1600 [#1606]: https://github.com/linebender/druid/pull/1606 +[#1619]: https://github.com/linebender/druid/pull/1619 +[#1634]: https://github.com/linebender/druid/pull/1634 [#1635]: https://github.com/linebender/druid/pull/1635 [Unreleased]: https://github.com/linebender/druid/compare/v0.7.0...master From 6a75243a24285e8f3da0e7bb129d910281cc4b12 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 18:23:16 -0500 Subject: [PATCH 08/16] formatting, adhering to standards --- druid/examples/list_sources.rs | 51 +++++++++++++--------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/druid/examples/list_sources.rs b/druid/examples/list_sources.rs index 53c37baa43..c5508ddd65 100644 --- a/druid/examples/list_sources.rs +++ b/druid/examples/list_sources.rs @@ -14,8 +14,7 @@ //! Demos alternative iterable data types for the List widget, im::HashMap and im::OrdMap -use druid::im::{hashmap, ordmap, vector, HashMap, OrdMap, Vector}; -use druid::lens::{self, LensExt}; +use druid::im::{hashmap, ordmap, HashMap, OrdMap}; use druid::widget::{Button, CrossAxisAlignment, Flex, Label, List, Scroll}; use druid::{ AppLauncher, Color, Data, Lens, LocalizedString, UnitPoint, Widget, WidgetExt, WindowDesc, @@ -67,16 +66,16 @@ fn ui_builder() -> impl Widget { data.adding_index += 1; data.hm_values.insert( data.adding_index as u32, - String::from(format!("Fruit #{}", data.adding_index)), + format!("Fruit #{}", data.adding_index), ); data.hm_keys_values - .insert(String::from(format!("Country #{}", data.adding_index)), 42); + .insert(format!("Country #{}", data.adding_index), 42); data.om_values.insert( data.adding_index as u32, - String::from(format!("Fruit #{}", data.adding_index)), + format!("Fruit #{}", data.adding_index), ); data.om_keys_values - .insert(String::from(format!("Country #{}", data.adding_index)), 42); + .insert(format!("Country #{}", data.adding_index), 42); }) .fix_height(30.0) .expand_width(), @@ -84,36 +83,24 @@ fn ui_builder() -> impl Widget { root.add_child( Button::new("Remove First") .on_click(|_, data: &mut AppData, _| { - if data.hm_values.len() > 0 { - match data.hm_values.clone().iter().next() { - Some(k) => { - data.hm_values.remove(&k.0.clone()); - } - _ => (), + if !data.hm_values.is_empty() { + if let Some(k) = data.hm_values.clone().iter().next() { + data.hm_values.remove(&k.0.clone()); } } - if data.hm_keys_values.len() > 0 { - match data.hm_keys_values.clone().iter().next() { - Some(k) => { - data.hm_keys_values.remove(&k.0.clone()); - } - _ => (), + if !data.hm_keys_values.is_empty() { + if let Some(k) = data.hm_keys_values.clone().iter().next() { + data.hm_keys_values.remove(&k.0.clone()); } } - if data.om_values.len() > 0 { - match data.om_values.clone().iter().next() { - Some(k) => { - data.om_values.remove(&k.0.clone()); - } - _ => (), + if !data.om_values.is_empty() { + if let Some(k) = data.om_values.clone().iter().next() { + data.om_values.remove(&k.0.clone()); } } - if data.om_keys_values.len() > 0 { - match data.om_keys_values.clone().iter().next() { - Some(k) => { - data.om_keys_values.remove(&k.0.clone()); - } - _ => (), + if !data.om_keys_values.is_empty() { + if let Some(k) = data.om_keys_values.clone().iter().next() { + data.om_keys_values.remove(&k.0.clone()); } } }) @@ -130,7 +117,7 @@ fn ui_builder() -> impl Widget { .with_child(Label::new("List from im::HashMap Values")) .with_child( Scroll::new(List::new(|| { - Label::new(|item: &String, _env: &_| format!("{}", item)) + Label::new(|item: &String, _env: &_| item.to_string()) .align_vertical(UnitPoint::LEFT) .padding(10.0) .expand() @@ -172,7 +159,7 @@ fn ui_builder() -> impl Widget { .with_child(Label::new("List from im::OrdMap Values")) .with_child( Scroll::new(List::new(|| { - Label::new(|item: &String, _env: &_| format!("{}", item)) + Label::new(|item: &String, _env: &_| item.to_string()) .align_vertical(UnitPoint::LEFT) .padding(10.0) .expand() From a8d193f401431f2a69ef176f7220330a25f0f0cd Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 18:26:23 -0500 Subject: [PATCH 09/16] adding changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5d18eb4d3..189178d8dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ You can find its changes [documented below](#070---2021-01-01). - Shell: windows implementation from content_insets ([#1592] by [@HoNile]) - Shell: IME API and macOS IME implementation ([#1619] by [@lord]) - Scroll::content_must_fill and a few other new Scroll methods ([#1635] by [@cmyr]) +- Added ListIter implementations for HashMap and OrdMap (tbd by [@Lejero]) ### Changed @@ -426,6 +427,7 @@ Last release without a changelog :( [@HoNile]: https://github.com/HoNile [@SecondFlight]: https://github.com/SecondFlight [@lord]: https://github.com/lord +[@Lejero]: https://github.com/Lejero [#599]: https://github.com/linebender/druid/pull/599 [#611]: https://github.com/linebender/druid/pull/611 From 5466798b80a8498890ad4d6b5e752826cdd61c6e Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Wed, 10 Mar 2021 18:30:27 -0500 Subject: [PATCH 10/16] Added PR --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 189178d8dc..f2bcf10558 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ You can find its changes [documented below](#070---2021-01-01). - Shell: windows implementation from content_insets ([#1592] by [@HoNile]) - Shell: IME API and macOS IME implementation ([#1619] by [@lord]) - Scroll::content_must_fill and a few other new Scroll methods ([#1635] by [@cmyr]) -- Added ListIter implementations for HashMap and OrdMap (tbd by [@Lejero]) +- Added ListIter implementations for HashMap and OrdMap ([#1641] by [@Lejero]) ### Changed @@ -634,6 +634,7 @@ Last release without a changelog :( [#1619]: https://github.com/linebender/druid/pull/1619 [#1634]: https://github.com/linebender/druid/pull/1634 [#1635]: https://github.com/linebender/druid/pull/1635 +[#1641]: https://github.com/linebender/druid/pull/1641 [Unreleased]: https://github.com/linebender/druid/compare/v0.7.0...master [0.7.0]: https://github.com/linebender/druid/compare/v0.6.0...v0.7.0 From 0967ccc68b1143c9f5e693d0b482cd8d2057e9f2 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Thu, 11 Mar 2021 13:42:46 -0500 Subject: [PATCH 11/16] Added web examples entry for list_sources.rs --- druid/examples/web/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/druid/examples/web/src/lib.rs b/druid/examples/web/src/lib.rs index f83c467ec9..ba32385df8 100644 --- a/druid/examples/web/src/lib.rs +++ b/druid/examples/web/src/lib.rs @@ -68,6 +68,7 @@ impl_example!(image); impl_example!(invalidation); impl_example!(layout); impl_example!(lens); +impl_example!(list_sources); impl_example!(list); impl_example!(multiwin); impl_example!(open_save); From cb039971309ab6ae40fbc0c80e7de0c31d912858 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Thu, 11 Mar 2021 16:15:40 -0500 Subject: [PATCH 12/16] Removed HashMap ListIter implementation. --- druid/examples/list_sources.rs | 76 ++++------------------------------ druid/src/widget/list.rs | 71 +------------------------------ 2 files changed, 9 insertions(+), 138 deletions(-) diff --git a/druid/examples/list_sources.rs b/druid/examples/list_sources.rs index c5508ddd65..d7d184420a 100644 --- a/druid/examples/list_sources.rs +++ b/druid/examples/list_sources.rs @@ -12,9 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Demos alternative iterable data types for the List widget, im::HashMap and im::OrdMap +//! Demos alternative iterable data type for the List widget, im::OrdMap +//! Two ListIter implementations provide a version which only concerns +//! itself with the values, and another which concerns itself with both +//! keys and vales. -use druid::im::{hashmap, ordmap, HashMap, OrdMap}; +use druid::im::{ordmap, OrdMap}; use druid::widget::{Button, CrossAxisAlignment, Flex, Label, List, Scroll}; use druid::{ AppLauncher, Color, Data, Lens, LocalizedString, UnitPoint, Widget, WidgetExt, WindowDesc, @@ -23,8 +26,6 @@ use druid::{ #[derive(Clone, Data, Lens)] struct AppData { adding_index: usize, - hm_values: HashMap, - hm_keys_values: HashMap, om_values: OrdMap, om_keys_values: OrdMap, } @@ -36,17 +37,12 @@ pub fn main() { LocalizedString::new("list-sources-demo-window-title") .with_placeholder("List Sources Demo"), ); - // Set our initial data. The HashMap and OrdMap types will behave differently ordering the same data. - let hm_values = hashmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; - let hm_keys_values = hashmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, - String::from("China") => 956960}; + // Set our initial data. let om_values = ordmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; let om_keys_values = ordmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, String::from("China") => 956960}; let data = AppData { - adding_index: hm_values.len(), - hm_values, - hm_keys_values, + adding_index: om_values.len(), om_values, om_keys_values, }; @@ -64,12 +60,6 @@ fn ui_builder() -> impl Widget { Button::new("Add") .on_click(|_, data: &mut AppData, _| { data.adding_index += 1; - data.hm_values.insert( - data.adding_index as u32, - format!("Fruit #{}", data.adding_index), - ); - data.hm_keys_values - .insert(format!("Country #{}", data.adding_index), 42); data.om_values.insert( data.adding_index as u32, format!("Fruit #{}", data.adding_index), @@ -83,16 +73,6 @@ fn ui_builder() -> impl Widget { root.add_child( Button::new("Remove First") .on_click(|_, data: &mut AppData, _| { - if !data.hm_values.is_empty() { - if let Some(k) = data.hm_values.clone().iter().next() { - data.hm_values.remove(&k.0.clone()); - } - } - if !data.hm_keys_values.is_empty() { - if let Some(k) = data.hm_keys_values.clone().iter().next() { - data.hm_keys_values.remove(&k.0.clone()); - } - } if !data.om_values.is_empty() { if let Some(k) = data.om_values.clone().iter().next() { data.om_values.remove(&k.0.clone()); @@ -110,48 +90,6 @@ fn ui_builder() -> impl Widget { let mut lists = Flex::row().cross_axis_alignment(CrossAxisAlignment::Start); - // Build a list of values from a hashmap - // The display order will be indeterminate. - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::HashMap Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &String, _env: &_| item.to_string()) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.5, 0.5)) - })) - .vertical() - .lens(AppData::hm_values), - ), - 1.0, - ); - - // Build a list of key value pairs from a hashmap - // The display order will be indeterminate. - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::HashMap Keys and Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &(String, u64), _env: &_| { - format!("{0}: {1} square kilometres", item.0, item.1) - }) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.0, 0.5)) - })) - .vertical() - .lens(AppData::hm_keys_values), - ), - 1.0, - ); - // Build a list values from an ordmap // The display order will be based on the Ord trait of the keys lists.add_flex_child( diff --git a/druid/src/widget/list.rs b/druid/src/widget/list.rs index aad9ca60d1..51267bb731 100644 --- a/druid/src/widget/list.rs +++ b/druid/src/widget/list.rs @@ -20,7 +20,7 @@ use std::f64; use std::sync::Arc; #[cfg(feature = "im")] -use crate::im::{HashMap, OrdMap, Vector}; +use crate::im::{OrdMap, Vector}; use crate::kurbo::{Point, Rect, Size}; @@ -118,69 +118,6 @@ impl ListIter for Vector { } #[cfg(feature = "im")] -//We sidestep dealing with ListIter<(K, V)> here. -//That would require us to deal with changing keys and key collisions. -impl ListIter for HashMap -where - K: Data + std::hash::Hash + std::cmp::Eq, - V: Data, -{ - fn for_each(&self, mut cb: impl FnMut(&V, usize)) { - for (i, item) in self.iter().enumerate() { - let ret = (item.0.to_owned(), item.1.to_owned()); - cb(&ret.1, i); - } - } - - fn for_each_mut(&mut self, mut cb: impl FnMut(&mut V, usize)) { - for (i, item) in self.iter_mut().enumerate() { - let mut ret = (item.0.clone(), item.1.clone()); - cb(&mut ret.1, i); - - if !item.1.same(&ret.1) { - *item.1 = ret.1; - } - } - } - - fn data_len(&self) -> usize { - self.len() - } -} - -#[cfg(feature = "im")] -//TODO: This implementation just disregards any changes to the key. It makes it visible, but nothing else. -impl ListIter<(K, V)> for HashMap -where - K: Data + std::hash::Hash + std::cmp::Eq, - V: Data, -{ - fn for_each(&self, mut cb: impl FnMut(&(K, V), usize)) { - for (i, item) in self.iter().enumerate() { - let ret = (item.0.to_owned(), item.1.to_owned()); - cb(&ret, i); - } - } - - fn for_each_mut(&mut self, mut cb: impl FnMut(&mut (K, V), usize)) { - for (i, item) in self.iter_mut().enumerate() { - let mut ret = (item.0.clone(), item.1.clone()); - cb(&mut ret, i); - - if !item.1.same(&ret.1) { - *item.1 = ret.1; - } - } - } - - fn data_len(&self) -> usize { - self.len() - } -} - -#[cfg(feature = "im")] -//We sidestep dealing with ListIter<(K, V)> here. -//That would require us to deal with changing keys and key collisions. impl ListIter for OrdMap where K: Data + Ord, @@ -198,11 +135,7 @@ where let mut ret = (item.0.clone(), item.1.clone()); cb(&mut ret.1, i); - //If item.0(Key) is different, we remove and reinsert with the new key. - if !item.0.same(&ret.0) { - self.remove(&item.0); - self.insert(ret.0, ret.1); - } else if !item.1.same(&ret.1) { + if !item.1.same(&ret.1) { self[&ret.0] = ret.1; } } From e261ab3af020aa021e20aa199d172fbdbd06eb28 Mon Sep 17 00:00:00 2001 From: Lejero <37251497+Lejero@users.noreply.github.com> Date: Thu, 11 Mar 2021 18:39:08 -0500 Subject: [PATCH 13/16] Update CHANGELOG.md Co-authored-by: Colin Rofls --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2bcf10558..fb0f9d5822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ You can find its changes [documented below](#070---2021-01-01). - Shell: windows implementation from content_insets ([#1592] by [@HoNile]) - Shell: IME API and macOS IME implementation ([#1619] by [@lord]) - Scroll::content_must_fill and a few other new Scroll methods ([#1635] by [@cmyr]) -- Added ListIter implementations for HashMap and OrdMap ([#1641] by [@Lejero]) +- Added ListIter implementations for OrdMap ([#1641] by [@Lejero]) ### Changed From 49741ac63e14b7e26dedbac0c4667882a89a1284 Mon Sep 17 00:00:00 2001 From: Lejero <37251497+Lejero@users.noreply.github.com> Date: Thu, 11 Mar 2021 18:39:14 -0500 Subject: [PATCH 14/16] Update druid/examples/list_sources.rs Co-authored-by: Colin Rofls --- druid/examples/list_sources.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/druid/examples/list_sources.rs b/druid/examples/list_sources.rs index d7d184420a..98f663ebce 100644 --- a/druid/examples/list_sources.rs +++ b/druid/examples/list_sources.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Druid Authors. +// Copyright 2021 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 8c1f29d5c34a75f76dc15cdec25c468f8609ddf1 Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Thu, 11 Mar 2021 18:50:27 -0500 Subject: [PATCH 15/16] Removed Unnecessary Clone --- druid/src/widget/list.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/druid/src/widget/list.rs b/druid/src/widget/list.rs index 51267bb731..9c49c8f256 100644 --- a/druid/src/widget/list.rs +++ b/druid/src/widget/list.rs @@ -97,7 +97,6 @@ pub trait ListIter: Data { /// Return data length. fn data_len(&self) -> usize; } - #[cfg(feature = "im")] impl ListIter for Vector { fn for_each(&self, mut cb: impl FnMut(&T, usize)) { @@ -132,11 +131,11 @@ where fn for_each_mut(&mut self, mut cb: impl FnMut(&mut V, usize)) { for (i, item) in self.clone().iter().enumerate() { - let mut ret = (item.0.clone(), item.1.clone()); - cb(&mut ret.1, i); + let mut ret = item.1.clone(); + cb(&mut ret, i); - if !item.1.same(&ret.1) { - self[&ret.0] = ret.1; + if !item.1.same(&ret) { + self[&item.0] = ret; } } } @@ -147,7 +146,6 @@ where } #[cfg(feature = "im")] -//TODO: This implementation just disregards any changes to the key. It makes it visible, but nothing else. impl ListIter<(K, V)> for OrdMap where K: Data + Ord, From 88794e0ed4339fefba27957009b91df8908fa52d Mon Sep 17 00:00:00 2001 From: Leah Roukema Date: Fri, 12 Mar 2021 08:20:02 -0500 Subject: [PATCH 16/16] Removed HashMap Key and Value impl for ListIter and list_sources.rs example --- druid/Cargo.toml | 4 - druid/examples/list_sources.rs | 138 --------------------------------- druid/examples/web/src/lib.rs | 1 - druid/src/widget/list.rs | 35 +-------- 4 files changed, 2 insertions(+), 176 deletions(-) delete mode 100644 druid/examples/list_sources.rs diff --git a/druid/Cargo.toml b/druid/Cargo.toml index 3c3f54ea06..c7131f9f85 100644 --- a/druid/Cargo.toml +++ b/druid/Cargo.toml @@ -97,10 +97,6 @@ required-features = ["image", "png"] name = "invalidation" required-features = ["im"] -[[example]] -name = "list_sources" -required-features = ["im"] - [[example]] name = "list" required-features = ["im"] diff --git a/druid/examples/list_sources.rs b/druid/examples/list_sources.rs deleted file mode 100644 index 98f663ebce..0000000000 --- a/druid/examples/list_sources.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2021 The Druid Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Demos alternative iterable data type for the List widget, im::OrdMap -//! Two ListIter implementations provide a version which only concerns -//! itself with the values, and another which concerns itself with both -//! keys and vales. - -use druid::im::{ordmap, OrdMap}; -use druid::widget::{Button, CrossAxisAlignment, Flex, Label, List, Scroll}; -use druid::{ - AppLauncher, Color, Data, Lens, LocalizedString, UnitPoint, Widget, WidgetExt, WindowDesc, -}; - -#[derive(Clone, Data, Lens)] -struct AppData { - adding_index: usize, - om_values: OrdMap, - om_keys_values: OrdMap, -} - -pub fn main() { - let main_window = WindowDesc::new(ui_builder()) - .window_size((1200.0, 600.0)) - .title( - LocalizedString::new("list-sources-demo-window-title") - .with_placeholder("List Sources Demo"), - ); - // Set our initial data. - let om_values = ordmap! {3 => String::from("Apple"), 1 => String::from("Pear"), 2 => String::from("Orange")}; - let om_keys_values = ordmap! {String::from("Russia") => 17098242, String::from("Canada") => 9984670, - String::from("China") => 956960}; - let data = AppData { - adding_index: om_values.len(), - om_values, - om_keys_values, - }; - AppLauncher::with_window(main_window) - .use_env_tracing() - .launch(data) - .expect("launch failed"); -} - -fn ui_builder() -> impl Widget { - let mut root = Flex::column(); - - //Buttons to test adding and removing entries from the lists - root.add_child( - Button::new("Add") - .on_click(|_, data: &mut AppData, _| { - data.adding_index += 1; - data.om_values.insert( - data.adding_index as u32, - format!("Fruit #{}", data.adding_index), - ); - data.om_keys_values - .insert(format!("Country #{}", data.adding_index), 42); - }) - .fix_height(30.0) - .expand_width(), - ); - root.add_child( - Button::new("Remove First") - .on_click(|_, data: &mut AppData, _| { - if !data.om_values.is_empty() { - if let Some(k) = data.om_values.clone().iter().next() { - data.om_values.remove(&k.0.clone()); - } - } - if !data.om_keys_values.is_empty() { - if let Some(k) = data.om_keys_values.clone().iter().next() { - data.om_keys_values.remove(&k.0.clone()); - } - } - }) - .fix_height(30.0) - .expand_width(), - ); - - let mut lists = Flex::row().cross_axis_alignment(CrossAxisAlignment::Start); - - // Build a list values from an ordmap - // The display order will be based on the Ord trait of the keys - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::OrdMap Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &String, _env: &_| item.to_string()) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.5, 0.5)) - })) - .vertical() - .lens(AppData::om_values), - ), - 1.0, - ); - - // Build a list of key value pairs from an ordmap - // The display order will be based on the Ord trait of the keys - lists.add_flex_child( - Flex::column() - .with_child(Label::new("List from im::OrdMap Keys and Values")) - .with_child( - Scroll::new(List::new(|| { - Label::new(|item: &(String, u64), _env: &_| { - format!("{0}: {1} square kilometres", item.0, item.1) - }) - .align_vertical(UnitPoint::LEFT) - .padding(10.0) - .expand() - .height(50.0) - .background(Color::rgb(0.5, 0.0, 0.5)) - })) - .vertical() - .lens(AppData::om_keys_values), - ), - 1.0, - ); - - root.add_flex_child(lists, 1.0); - - root -} diff --git a/druid/examples/web/src/lib.rs b/druid/examples/web/src/lib.rs index ba32385df8..f83c467ec9 100644 --- a/druid/examples/web/src/lib.rs +++ b/druid/examples/web/src/lib.rs @@ -68,7 +68,6 @@ impl_example!(image); impl_example!(invalidation); impl_example!(layout); impl_example!(lens); -impl_example!(list_sources); impl_example!(list); impl_example!(multiwin); impl_example!(open_save); diff --git a/druid/src/widget/list.rs b/druid/src/widget/list.rs index 9c49c8f256..56d39a4200 100644 --- a/druid/src/widget/list.rs +++ b/druid/src/widget/list.rs @@ -116,6 +116,8 @@ impl ListIter for Vector { } } +//An implementation for ListIter<(K, V)> has been ommitted due to problems +//with how the List Widget handles the reordering of its data. #[cfg(feature = "im")] impl ListIter for OrdMap where @@ -145,39 +147,6 @@ where } } -#[cfg(feature = "im")] -impl ListIter<(K, V)> for OrdMap -where - K: Data + Ord, - V: Data, -{ - fn for_each(&self, mut cb: impl FnMut(&(K, V), usize)) { - for (i, item) in self.iter().enumerate() { - let ret = (item.0.to_owned(), item.1.to_owned()); - cb(&ret, i); - } - } - - fn for_each_mut(&mut self, mut cb: impl FnMut(&mut (K, V), usize)) { - for (i, item) in self.clone().iter().enumerate() { - let mut ret = (item.0.clone(), item.1.clone()); - cb(&mut ret, i); - - //If item.0(Key) is different, we remove and reinsert with the new key. - if !item.0.same(&ret.0) { - self.remove(&item.0); - self.insert(ret.0, ret.1); - } else if !item.1.same(&ret.1) { - self[&ret.0] = ret.1; - } - } - } - - fn data_len(&self) -> usize { - self.len() - } -} - // S == shared data type #[cfg(feature = "im")] impl ListIter<(S, T)> for (S, Vector) {