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

Add 'im' feature for supporting the im crate collections #924

Merged
merged 1 commit into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path=druid/Cargo.toml --all-targets --features=svg,image -- -D warnings
args: --manifest-path=druid/Cargo.toml --all-targets --features=svg,image,im -- -D warnings

- name: cargo clippy druid-derive
uses: actions-rs/cargo@v1
Expand All @@ -91,7 +91,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path=druid/Cargo.toml --features=svg,image
args: --manifest-path=druid/Cargo.toml --features=svg,image,im

- name: cargo test druid-derive
uses: actions-rs/cargo@v1
Expand Down Expand Up @@ -176,7 +176,7 @@ jobs:
with:
command: clippy
# TODO: Add svg feature when it's no longer broken with wasm
args: --manifest-path=druid/Cargo.toml --all-targets --features=image --target wasm32-unknown-unknown -- -D warnings
args: --manifest-path=druid/Cargo.toml --all-targets --features=image,im --target wasm32-unknown-unknown -- -D warnings

- name: cargo clippy druid-derive
uses: actions-rs/cargo@v1
Expand All @@ -203,7 +203,7 @@ jobs:
with:
command: test
# TODO: Add svg feature when it's no longer broken with wasm
args: --manifest-path=druid/Cargo.toml --features=image --no-run --target wasm32-unknown-unknown
args: --manifest-path=druid/Cargo.toml --features=image,im --no-run --target wasm32-unknown-unknown

- name: cargo test compile druid-derive
uses: actions-rs/cargo@v1
Expand Down Expand Up @@ -264,7 +264,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path=druid/Cargo.toml --features=svg,image
args: --manifest-path=druid/Cargo.toml --features=svg,image,im

- name: cargo test druid-derive
uses: actions-rs/cargo@v1
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ 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])

### Changed

Expand Down Expand Up @@ -165,6 +166,7 @@ While some features like the clipboard, menus or file dialogs are not yet availa
[#909]: https://github.com/xi-editor/druid/pull/909
[#917]: https://github.com/xi-editor/druid/pull/917
[#920]: https://github.com/xi-editor/druid/pull/920
[#924]: https://github.com/xi-editor/druid/pull/924

## [0.5.0] - 2020-04-01

Expand Down
55 changes: 55 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions docs/src/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,17 @@ Here is an example of using `Data` to implement a simple data model.
```rust
{{#include ../book_examples/src/data_md.rs:derive}}
```

#### Collections

`Data` is expected to be cheap to clone and cheap to compare, which can cause
issues with collection types. For this reason, `Data` is not implemented for
`std` types like `Vec` or `HashMap`. This is not a huge issue, however; you can
always put these types inside an `Rc` or an `Arc`, or if you're dealing with
larger collections you can build druid with the `im` feature, which brings in
the [`im crate`], and adds a `Data` impl for the collections there. The [`im`
crate] is a collection of immutable data structures that act a lot like the `std`
collections, but can be cloned efficiently.


[`im` crate]: https://docs.rs/im
1 change: 1 addition & 0 deletions druid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fnv = "1.0.3"
xi-unicode = "0.2.0"
image = {version = "0.23.2", optional = true}
instant = { version = "0.1", features = [ "wasm-bindgen" ] }
im = { version = "14.0", optional = true }

[dependencies.simple_logger]
version = "1.6.0"
Expand Down
84 changes: 84 additions & 0 deletions druid/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ pub use druid_derive::Data;
/// This function must have a signature in the form, `fn<T>(&T, &T) -> bool`,
/// where `T` is the type of the field.
///
/// ## Collection types
///
/// `Data` is not implemented for `std` collection types, because comparing them
/// can be expensive. To use collection types with druid, there are two easy options:
/// either wrap the collection in an `Arc`, or build druid with the `im` feature,
/// which adds `Data` implementations to the collections from the [`im` crate],
/// a set of immutable data structures that fit nicely with druid.
///
/// If the `im` feature is used, the `im` crate is reexported from the root
/// of the druid crate.
///
/// ### Example:
///
/// ```
Expand All @@ -90,6 +101,7 @@ pub use druid_derive::Data;
/// checks for equality. Therefore, such types must also implement `PartialEq`.
///
/// [`Data::same`]: trait.Data.html#tymethod.same
/// [`im` crate]: https://docs.rs/im
pub trait Data: Clone + 'static {
//// ANCHOR: same_fn
/// Determine whether two values are the same.
Expand Down Expand Up @@ -375,6 +387,58 @@ impl Data for piet::Color {
}
}

//FIXME Vector::ptr_eq is not currently reliable; this is a temporary impl?
#[cfg(feature = "im")]
impl<T: Data> Data for im::Vector<T> {
fn same(&self, other: &Self) -> bool {
// for reasons outlined in https://github.com/bodil/im-rs/issues/129,
// ptr_eq always returns false for small collections. This heuristic
// falls back to using equality for collections below some threshold.
// There may be a possibility of this returning false negatives, but
// not false positives; that's an acceptable outcome.

/* this is the impl I expected to use
const INLINE_LEN: usize = 48; // bytes available before first allocation;
let inline_capacity: usize = INLINE_LEN / std::mem::size_of::<T>();
if self.len() == other.len() && self.len() <= inline_capacity {
self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
} else {
self.ptr_eq(other)
}
*/

self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
}
}

#[cfg(feature = "im")]
impl<K: Clone + 'static, V: Data> Data for im::HashMap<K, V> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

#[cfg(feature = "im")]
impl<T: Data> Data for im::HashSet<T> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

#[cfg(feature = "im")]
impl<K: Clone + 'static, V: Data> Data for im::OrdMap<K, V> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

#[cfg(feature = "im")]
impl<T: Data> Data for im::OrdSet<T> {
fn same(&self, other: &Self) -> bool {
self.ptr_eq(other)
}
}

macro_rules! impl_data_for_array {
() => {};
($this:tt $($rest:tt)*) => {
Expand Down Expand Up @@ -406,4 +470,24 @@ mod test {
assert!(input.same(&[1u8, 0, 0, 1, 0]));
assert!(!input.same(&[1u8, 1, 0, 1, 0]));
}

#[test]
#[cfg(feature = "im")]
fn im_data() {
for len in 8..256 {
let input = std::iter::repeat(0_u8).take(len).collect::<im::Vector<_>>();
let mut inp2 = input.clone();
assert!(input.same(&inp2));
inp2.set(len - 1, 98);
assert!(!input.same(&inp2));
}
}

#[test]
#[cfg(feature = "im")]
fn im_vec_different_length() {
let one = std::iter::repeat(0_u8).take(9).collect::<im::Vector<_>>();
let two = std::iter::repeat(0_u8).take(10).collect::<im::Vector<_>>();
assert!(!one.same(&two));
}
}
5 changes: 5 additions & 0 deletions druid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ use druid_shell as shell;
#[doc(inline)]
pub use druid_shell::{kurbo, piet};

// the im crate provides immutable data structures that play well with druid
#[cfg(feature = "im")]
#[doc(inline)]
pub use im;

mod app;
mod app_delegate;
mod bloom;
Expand Down