Dataflow and the Data trait

The Druid architecture is based on a two-way dataflow.

At the root level, you define the application state, which is passed to each child widget as associated data. Some Widgets (eg LensWrap) will only pass a subset of that data to their children.

Some widgets (eg Button, TextBox, Checkbox) can mutate the data passed to them by their parents in reaction to user events. The data mutated in a child widget is also changed in the parent widgets, all the way to the root.

When you mutate a widget's associated data, Druid compares the old and new version, and propagates the change to the widgets that are affected by the change.

Note that, in all that workflow, Widgets don't actually store their associated data. A Button<Vector<String>> doesn't actually store a Vector<String>, instead the framework stores one per button, which is provided to widget methods.

For this to work, your model must implement the Clone and Data traits. The Data trait has a single method:

    /// Determine whether two values are the same.
    ///
    /// This is intended to always be a fast operation. If it returns
    /// `true`, the two values *must* be equal, but two equal values
    /// need not be considered the same here, as will often be the
    /// case when two copies are separately allocated.
    ///
    /// Note that "equal" above has a slightly different meaning than
    /// `PartialEq`, for example two floating point NaN values should
    /// be considered equal when they have the same bit representation.
    fn same(&self, other: &Self) -> bool;

This method checks for equality, but allows for false negatives.

Performance

It is important that your data is cheap to clone and cheap to compare; we encourage the use of reference counted pointers to allow cheap cloning of more expensive types. Arc and Rc have blanket Data impls that do pointer comparison, so if you have a type that does not implement Data, you can always just wrap it in one of those smart pointers.

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.

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.

Derive

Data can be derived. This is recursive; it requires Data to be implemented for all members. For 'C style' enums (enums where no variant has any fields) this also requires an implementation of PartialEq. Data is implemented for a number of std types, including all primitive types, String, Arc, Rc, as well as Option, Result, and various tuples whose members implement Data.

Here is an example of using Data to implement a simple data model:


#![allow(unused)]
fn main() {
use druid::Data;
use std::sync::Arc;

#[derive(Clone, Data)]
/// The main model for a todo list application.
struct TodoList {
    items: Arc<Vec<TodoItem>>,
}

#[derive(Clone, Data)]
/// A single todo item.
struct TodoItem {
    category: Category,
    title: String,
    note: Option<String>,
    completed: bool,

    // `Data` is implemented for any `Arc`.
    due_date: Option<Arc<DateTime>>,

    // You can specify a custom comparison fn
    // (anything with the signature (&T, &T) -> bool).
    #[data(same_fn = "PartialEq::eq")]
    added_date: DateTime,

    // You can specify that a field should
    // be skipped when computing same-ness
    #[data(ignore)]
    debug_timestamp: usize,
}

#[derive(Clone, Data, PartialEq)]
/// The three types of tasks in the world.
enum Category {
    Work,
    Play,
    Revolution,
}
}

Mapping Data with lenses

In Druid, most container widgets expect their children to have the same associated data. If you have a Flex<Foobar>, you can only append widgets that implement Widget<Foobar> to it.

In some cases, however, you want to compose widgets that operate on different subsets of the data. Maybe you want to add two widgets to the above Flex, one that uses the field foo and another that uses the field bar, and they might respectively implement Widget<Foo> and Widget<Bar>.

Lenses allow you to bridge that type difference. A lens is a type that represents a two-way mapping between two data types. That is, a lens from X to Y can take an instance of X and give you an instance of Y, and can take a modified Y and apply the modification to X.

To expand on our Foobar example:


#![allow(unused)]
fn main() {
#[derive(Lens)]
struct Foobar {
    foo: Foo,
    bar: Bar,
}
}

The derive macro above generates two lenses: Foobar::foo and Foobar::bar. Foobar::foo can take an instance of Foobar and give you a shared or mutable reference to its foo field. Finally, the type LensWrap can take that lens and use it to map between different widget types:


#![allow(unused)]
fn main() {
fn build_foo() -> impl Widget<Foo> {
    // ...
}

fn build_bar() -> impl Widget<Bar> {

}

fn build_foobar() -> impl Widget<Foobar> {
    Flex::column()
        .with_child(
            LensWrap::new(build_foo(), Foobar::foo),
        )
        .with_child(
            LensWrap::new(build_bar(), Foobar::bar),
        )
}
}

See the Lens chapter for a more in-depth explanation of what lenses are and how they're implemented.