Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listiter impl #1641

Merged
merged 18 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ([#1641] by [@Lejero])
Lejero marked this conversation as resolved.
Show resolved Hide resolved

### Changed

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -632,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
Expand Down
4 changes: 4 additions & 0 deletions druid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ required-features = ["image", "png"]
name = "invalidation"
required-features = ["im"]

[[example]]
name = "list_sources"
required-features = ["im"]

[[example]]
name = "list"
required-features = ["im"]
Expand Down
138 changes: 138 additions & 0 deletions druid/examples/list_sources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright 2019 The Druid Authors.
Lejero marked this conversation as resolved.
Show resolved Hide resolved
//
// 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<u32, String>,
om_keys_values: OrdMap<String, u64>,
}

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<AppData> {
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
}
1 change: 1 addition & 0 deletions druid/examples/web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
65 changes: 64 additions & 1 deletion druid/src/widget/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::f64;
use std::sync::Arc;

#[cfg(feature = "im")]
use crate::im::Vector;
use crate::im::{OrdMap, Vector};

use crate::kurbo::{Point, Rect, Size};

Expand Down Expand Up @@ -117,6 +117,69 @@ impl<T: Data> ListIter<T> for Vector<T> {
}
}

#[cfg(feature = "im")]
impl<K, V> ListIter<V> for OrdMap<K, V>
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());
Lejero marked this conversation as resolved.
Show resolved Hide resolved
cb(&mut ret.1, i);

if !item.1.same(&ret.1) {
self[&ret.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.
Lejero marked this conversation as resolved.
Show resolved Hide resolved
impl<K, V> ListIter<(K, V)> for OrdMap<K, V>
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<S: Data, T: Data> ListIter<(S, T)> for (S, Vector<T>) {
Expand Down