Skip to content

Commit

Permalink
Add im::Vector support to the List widget. (#940)
Browse files Browse the repository at this point in the history
  • Loading branch information
xStrom authored May 15, 2020
1 parent 1f56a17 commit 57a0c35
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 111 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ While some features like the clipboard, menus or file dialogs are not yet availa
- `UpdateCtx::request_timer` and `UpdateCtx::request_anim_frame`. ([#898] by [@finnerale])
- `UpdateCtx::size` and `LifeCycleCtx::size`. ([#917] by [@jneem])
- `WidgetExt::debug_widget_id`, for displaying widget ids on hover. ([#876] by [@cmyr])
- `im` feature, with `Data` support for the [`im` crate](https://docs.rs/im/) collections. ([#924])
- `im` feature, with `Data` support for the [`im` crate](https://docs.rs/im/) collections. ([#924] by [@cmyr])
- `im::Vector` support for the `List` widget. ([#940] by [@xStrom])

### Changed

Expand Down Expand Up @@ -171,6 +172,7 @@ While some features like the clipboard, menus or file dialogs are not yet availa
[#924]: https://github.com/xi-editor/druid/pull/924
[#925]: https://github.com/xi-editor/druid/pull/925
[#928]: https://github.com/xi-editor/druid/pull/928
[#940]: https://github.com/xi-editor/druid/pull/940

## [0.5.0] - 2020-04-01

Expand Down
197 changes: 105 additions & 92 deletions druid/examples/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,109 +14,122 @@

//! Demos basic list widget and list manipulations.
use std::sync::Arc;

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 {
left: Arc<Vec<u32>>,
right: Arc<Vec<u32>>,
#[cfg(not(feature = "im"))]
pub fn main() {
eprintln!("This examples requires the \"im\" feature to be enabled:");
eprintln!("cargo run --example list --features im");
}

#[cfg(feature = "im")]
pub fn main() {
let main_window = WindowDesc::new(ui_builder)
.title(LocalizedString::new("list-demo-window-title").with_placeholder("List Demo"));
// Set our initial data
let data = AppData {
left: Arc::new(vec![1, 2]),
right: Arc::new(vec![1, 2, 3]),
};
AppLauncher::with_window(main_window)
.use_simple_logger()
.launch(data)
.expect("launch failed");
example::main()
}

fn ui_builder() -> impl Widget<AppData> {
let mut root = Flex::column();
#[cfg(feature = "im")]
mod example {
use druid::im::{vector, 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 {
left: Vector<u32>,
right: Vector<u32>,
}

pub fn main() {
let main_window = WindowDesc::new(ui_builder)
.title(LocalizedString::new("list-demo-window-title").with_placeholder("List Demo"));
// Set our initial data
let data = AppData {
left: vector![1, 2],
right: vector![1, 2, 3],
};
AppLauncher::with_window(main_window)
.use_simple_logger()
.launch(data)
.expect("launch failed");
}

fn ui_builder() -> impl Widget<AppData> {
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
let value = data.left.len() + 1;
Arc::make_mut(&mut data.left).push(value as u32);
// 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
let value = data.left.len() + 1;
data.left.push_back(value as u32);

// Add child to right list
let value = data.right.len() + 1;
Arc::make_mut(&mut data.right).push(value as u32);
})
.fix_height(30.0)
.expand_width(),
);
// Add child to right list
let value = data.right.len() + 1;
data.right.push_back(value as u32);
})
.fix_height(30.0)
.expand_width(),
);

let mut lists = Flex::row().cross_axis_alignment(CrossAxisAlignment::Start);
let mut lists = Flex::row().cross_axis_alignment(CrossAxisAlignment::Start);

// Build a simple list
lists.add_flex_child(
Scroll::new(List::new(|| {
Label::new(|item: &u32, _env: &_| format!("List item #{}", item))
.align_vertical(UnitPoint::LEFT)
.padding(10.0)
.expand()
.height(50.0)
.background(Color::rgb(0.5, 0.5, 0.5))
}))
.vertical()
.lens(AppData::left),
1.0,
);
// Build a simple list
lists.add_flex_child(
Scroll::new(List::new(|| {
Label::new(|item: &u32, _env: &_| format!("List item #{}", item))
.align_vertical(UnitPoint::LEFT)
.padding(10.0)
.expand()
.height(50.0)
.background(Color::rgb(0.5, 0.5, 0.5))
}))
.vertical()
.lens(AppData::left),
1.0,
);

// Build a list with shared data
lists.add_flex_child(
Scroll::new(List::new(|| {
Flex::row()
.with_child(
Label::new(|(_, item): &(Arc<Vec<u32>>, 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 (Arc<Vec<u32>>, u32), _env| {
// We have access to both child's data and shared data.
// Remove element from right list.
Arc::make_mut(shared).retain(|v| v != item);
// Build a list with shared data
lists.add_flex_child(
Scroll::new(List::new(|| {
Flex::row()
.with_child(
Label::new(|(_, item): &(Vector<u32>, u32), _env: &_| {
format!("List item #{}", 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)
}))
.vertical()
.lens(lens::Id.map(
// Expose shared data with children data
|d: &AppData| (d.right.clone(), d.right.clone()),
|d: &mut AppData, x: (Arc<Vec<u32>>, Arc<Vec<u32>>)| {
// If shared data was changed reflect the changes in our AppData
d.right = x.0
},
)),
1.0,
);
.align_vertical(UnitPoint::LEFT),
)
.with_flex_spacer(1.0)
.with_child(
Button::new("Delete")
.on_click(|_ctx, (shared, item): &mut (Vector<u32>, 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)
}))
.vertical()
.lens(lens::Id.map(
// Expose shared data with children data
|d: &AppData| (d.right.clone(), d.right.clone()),
|d: &mut AppData, x: (Vector<u32>, Vector<u32>)| {
// 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.add_flex_child(lists, 1.0);

// Mark the widget as needing its layout rects painted
root.debug_paint_layout()
// Mark the widget as needing its layout rects painted
root.debug_paint_layout()
}
}
13 changes: 0 additions & 13 deletions druid/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,6 @@ impl<T: Data, U: Data> Data for Result<T, U> {
}
}

impl<D: Data> Data for Vec<D> {
fn same(&self, other: &Self) -> bool {
self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
}
}

impl Data for () {
fn same(&self, _other: &Self) -> bool {
true
Expand Down Expand Up @@ -457,13 +451,6 @@ impl_data_for_array! { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 }
mod test {
use super::Data;

#[test]
fn vec_data() {
let input = vec![1u8, 0, 0, 1, 0];
assert!(input.same(&vec![1u8, 0, 0, 1, 0]));
assert!(!input.same(&vec![1u8, 1, 0, 1, 0]));
}

#[test]
fn array_data() {
let input = [1u8, 0, 0, 1, 0];
Expand Down
61 changes: 56 additions & 5 deletions druid/src/widget/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
use std::cmp::Ordering;
use std::sync::Arc;

#[cfg(feature = "im")]
use crate::im::Vector;

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

use crate::{
Expand Down Expand Up @@ -71,6 +74,54 @@ pub trait ListIter<T>: Data {
fn data_len(&self) -> usize;
}

#[cfg(feature = "im")]
impl<T: Data> ListIter<T> for Vector<T> {
fn for_each(&self, mut cb: impl FnMut(&T, usize)) {
for (i, item) in self.iter().enumerate() {
cb(item, i);
}
}

fn for_each_mut(&mut self, mut cb: impl FnMut(&mut T, usize)) {
for (i, item) in self.iter_mut().enumerate() {
cb(item, i);
}
}

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>) {
fn for_each(&self, mut cb: impl FnMut(&(S, T), usize)) {
for (i, item) in self.1.iter().enumerate() {
let d = (self.0.to_owned(), item.to_owned());
cb(&d, i);
}
}

fn for_each_mut(&mut self, mut cb: impl FnMut(&mut (S, T), usize)) {
for (i, item) in self.1.iter_mut().enumerate() {
let mut d = (self.0.clone(), item.clone());
cb(&mut d, i);

if !self.0.same(&d.0) {
self.0 = d.0;
}
if !item.same(&d.1) {
*item = d.1;
}
}
}

fn data_len(&self) -> usize {
self.1.len()
}
}

impl<T: Data> ListIter<T> for Arc<Vec<T>> {
fn for_each(&self, mut cb: impl FnMut(&T, usize)) {
for (i, item) in self.iter().enumerate() {
Expand Down Expand Up @@ -102,16 +153,16 @@ impl<T: Data> ListIter<T> for Arc<Vec<T>> {
}
}

impl<T1: Data, T: Data> ListIter<(T1, T)> for (T1, Arc<Vec<T>>) {
fn for_each(&self, mut cb: impl FnMut(&(T1, T), usize)) {
// S == shared data type
impl<S: Data, T: Data> ListIter<(S, T)> for (S, Arc<Vec<T>>) {
fn for_each(&self, mut cb: impl FnMut(&(S, T), usize)) {
for (i, item) in self.1.iter().enumerate() {
let d = (self.0.clone(), item.to_owned());
cb(&d, i);
}
}

fn for_each_mut(&mut self, mut cb: impl FnMut(&mut (T1, T), usize)) {
let shared = self.0.to_owned();
fn for_each_mut(&mut self, mut cb: impl FnMut(&mut (S, T), usize)) {
let mut new_data = Vec::with_capacity(self.1.len());
let mut any_shared_changed = false;
let mut any_el_changed = false;
Expand All @@ -120,7 +171,7 @@ impl<T1: Data, T: Data> ListIter<(T1, T)> for (T1, Arc<Vec<T>>) {
let mut d = (self.0.clone(), item.to_owned());
cb(&mut d, i);

if !any_shared_changed && !shared.same(&d.0) {
if !any_shared_changed && !self.0.same(&d.0) {
any_shared_changed = true;
}
if any_shared_changed {
Expand Down

0 comments on commit 57a0c35

Please sign in to comment.