The Env

The Env represents the environment; it is intended as a way of managing and accessing state about your specific application, such as color schemes, localized strings, and other resources.

The Env is created when the application is launched, and is passed down to all widgets. The Env may be modified at various points in the tree; values in the environment can be overridden with other values of the same type, but they can never be removed. If something exists in the Env at a given level of the tree, it will exist for everything 'below' that level; that is, for all children of that widget.

Keys, Values, and themes

The most prominent role of Env is to store a set of typed keys and values. The Env can only store a few types of things; these are represented by the Value type, which looks like this:

pub enum Value {
    Point(Point),
    Size(Size),
    Rect(Rect),
    Insets(Insets),
    Color(Color),
    Float(f64),
    Bool(bool),
    UnsignedInt(u64),
    String(ArcStr),
    Font(FontDescriptor),
    RoundedRectRadii(RoundedRectRadii),
    Other(Arc<dyn Any + Send + Sync>),
}

The only way to get an item out of the Env is with a Key. A Key is a combination of a string identifier and a type.

You can think of this as strict types, enforced at runtime. It is the programmer's responsibility to ensure that the key used to get a value has the same type as the one used to set it. The API is aggressive about checking for misuse, and many methods will panic if anything is amiss. In practice this shouldn't almost never happen if you follow these guidelines:

  1. Keys should be consts with unique names. If you need to use a custom key, you should declare it as a const, and give it a unique name. By convention, you should namespace your keys using something like reverse-DNS notation, or even just prefixing them with the name of your app.

    const BAD_NAME: Key<f64> = Key::new("height");
    const GOOD_NAME: Key<f64> = Key::new("com.example.my-app.main-view-height");
  2. Keys must always be set before they are used. In practice this means that most keys are set when your application launches, using AppLauncher::configure_env. Once a key has been added to the Env, it cannot be deleted, although it can be overwritten.

  3. Values can only be overwritten by values of the same type. If you have a Key<f64>, assuming that key has already been added to the Env, you cannot replace it with any other type.

Assuming these rules are followed, Env should just work.

KeyOrValue

Druid includes a KeyOrValue type that is used for setting certain properties of widgets. This is a type that can be either a concrete instance of some type, or a Key that can be used to get that type from the Env.

const IMPORTANT_LABEL_COLOR: Key<Color> = Key::new("org.linebender.example.important-label-color");
const RED: Color = Color::rgb8(0xFF, 0, 0);

fn make_labels() {
    let with_value = Label::<()>::new("Warning!").with_text_color(RED);
    let with_key = Label::<()>::new("Warning!").with_text_color(IMPORTANT_LABEL_COLOR);
}

EnvScope

You may override values in the environment for a given widget (and all of its children) by using the EnvScope widget. This is easiest when combined with the env_scope method on WidgetExt:

fn scoped_label() {
    let my_label = Label::<()>::new("Warning!").env_scope(|env, _| {
        env.set(druid::theme::TEXT_COLOR, Color::BLACK);
        env.set(druid::theme::TEXT_SIZE_NORMAL, 18.0);
    });
}

Localization

Localization is currently half-baked.

The Env contains the localization resources for the current locale. A LocalizedString can be resolved to a given string in the current locale by calling its resolve method.