Struct druid::env::Env[][src]

pub struct Env(_);
Expand description

An environment passed down through all widget traversals.

All widget methods have access to an environment, and it is passed downwards during traversals.

A widget can retrieve theme parameters (colors, dimensions, etc.). In addition, it can pass custom data down to all descendants. An important example of the latter is setting a value for enabled/disabled status so that an entire subtree can be disabled (“grayed out”) with one setting.

EnvScope can be used to override parts of Env for its descendants.

Important

It is the programmer’s responsibility to ensure that the environment is used correctly. See Key for an example.

  • Keys should be consts with unique names
  • Keys must always be set before they are used.
  • Values can only be overwritten by values of the same type.

Implementations

A key used to tell widgets to print additional debug information.

This does nothing by default; however you can check this key while debugging a widget to limit println spam.

For convenience, this key can be set with the WidgetExt::debug_widget method.

Examples
if env.get(Env::DEBUG_WIDGET) {
    eprintln!("widget {:?} bounds: {:?}", widget_id, my_rect);
}

Gets a value from the environment, expecting it to be present.

Note that the return value is a reference for “expensive” types such as strings, but an ordinary value for “cheap” types such as numbers and colors.

Panics

Panics if the key is not found, or if it is present with the wrong type.

Examples found in repository
druid/examples/calc.rs (line 125)
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
fn op_button_label(op: char, label: String) -> impl Widget<CalcState> {
    let painter = Painter::new(|ctx, _, env| {
        let bounds = ctx.size().to_rect();

        ctx.fill(bounds, &env.get(theme::PRIMARY_DARK));

        if ctx.is_hot() {
            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
        }

        if ctx.is_active() {
            ctx.fill(bounds, &env.get(theme::PRIMARY_LIGHT));
        }
    });

    Label::new(label)
        .with_text_size(24.)
        .center()
        .background(painter)
        .expand()
        .on_click(move |_ctx, data: &mut CalcState, _env| data.op(op))
}

fn op_button(op: char) -> impl Widget<CalcState> {
    op_button_label(op, op.to_string())
}

fn digit_button(digit: u8) -> impl Widget<CalcState> {
    let painter = Painter::new(|ctx, _, env| {
        let bounds = ctx.size().to_rect();

        ctx.fill(bounds, &env.get(theme::BACKGROUND_LIGHT));

        if ctx.is_hot() {
            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
        }

        if ctx.is_active() {
            ctx.fill(bounds, &Color::rgb8(0x71, 0x71, 0x71));
        }
    });

    Label::new(format!("{}", digit))
        .with_text_size(24.)
        .center()
        .background(painter)
        .expand()
        .on_click(move |_ctx, data: &mut CalcState, _env| data.digit(digit))
}
More examples
druid/examples/sub_window.rs (line 284)
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), env: &Env) {
        let sz = ctx.size();

        let monitors = Screen::get_monitors();
        let all = monitors
            .iter()
            .map(|x| x.virtual_rect())
            .fold(Rect::ZERO, |s, r| r.union(s));
        if all.width() > 0. && all.height() > 0. {
            let trans = Affine::scale(f64::min(sz.width / all.width(), sz.height / all.height()))
                * Affine::translate(all.origin().to_vec2()).inverse();
            let font = env.get(theme::UI_FONT).family;

            for (i, mon) in monitors.iter().enumerate() {
                let vr = mon.virtual_rect();
                let tr = trans.transform_rect_bbox(vr);
                ctx.stroke(tr, &Color::WHITE, 1.0);

                if let Ok(tl) = ctx
                    .text()
                    .new_text_layout(format!(
                        "{}:{}x{}@{},{}",
                        i,
                        vr.width(),
                        vr.height(),
                        vr.x0,
                        vr.y0
                    ))
                    .max_width(tr.width() - 5.)
                    .font(font.clone(), env.get(theme::TEXT_SIZE_NORMAL))
                    .text_color(Color::WHITE)
                    .build()
                {
                    ctx.draw_text(&tl, tr.center() - tl.size().to_vec2() * 0.5);
                }
            }
        }
    }
}

struct CancelClose;

impl<W: Widget<bool>> Controller<bool, W> for CancelClose {
    fn event(
        &mut self,
        w: &mut W,
        ctx: &mut EventCtx<'_, '_>,
        event: &Event,
        data: &mut bool,
        env: &Env,
    ) {
        match (&data, event) {
            (false, Event::WindowCloseRequested) => ctx.set_handled(),
            _ => w.event(ctx, event, data, env),
        }
    }
}

fn build_root_widget() -> impl Widget<HelloState> {
    let label = EnvScope::new(
        |env, _t| env.set(theme::TEXT_COLOR, env.get(theme::PRIMARY_LIGHT)),
        ControllerHost::new(
            Label::new(|data: &HelloState, _env: &Env| {
                format!("Hello {}! {} ", data.name, data.sub.my_stuff)
            }),
            TooltipController::new("Tips! Are good"),
        ),
    );
    // a textbox that modifies `name`.
    let textbox = TextBox::new()
        .with_placeholder("Who are we greeting?")
        .fix_width(TEXT_BOX_WIDTH)
        .lens(HelloState::sub.then(SubState::my_stuff));

    let button = Button::new("Make sub window")
        .on_click(|ctx, data: &mut SubState, env| {
            let tb = TextBox::new().lens(SubState::my_stuff);
            let drag_thing = Label::new("Drag me").controller(DragWindowController::new());
            let col = Flex::column().with_child(drag_thing).with_child(tb);

            ctx.new_sub_window(
                WindowConfig::default()
                    .show_titlebar(false)
                    .window_size(Size::new(100., 100.))
                    .set_position(Point::new(1000.0, 500.0))
                    .set_level(WindowLevel::AppWindow),
                col,
                data.clone(),
                env.clone(),
            );
        })
        .center()
        .lens(HelloState::sub);

    let check_box =
        ControllerHost::new(Checkbox::new("Closeable?"), CancelClose).lens(HelloState::closeable);
    // arrange the two widgets vertically, with some padding
    let layout = Flex::column()
        .with_child(label)
        .with_flex_child(ScreenThing.lens(Unit::default()).padding(5.), 1.)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(textbox)
        .with_child(button)
        .with_child(check_box);

    // center the two widgets in the available space
    Align::centered(layout)
}

Trys to get a value from the environment.

If the value is not found, the raw key is returned as the error.

Panics

Panics if the value for the key is found, but has the wrong type.

Gets a value from the environment, in its encapsulated Value form, expecting the key to be present.

WARNING: This is not intended for general use, but only for inspecting an Env e.g. for debugging, theme editing, and theme loading.

Panics

Panics if the key is not found.

Gets a value from the environment, in its encapsulated Value form, returning None if a value isn’t found.

Note

This is not intended for general use, but only for inspecting an Env e.g. for debugging, theme editing, and theme loading.

Gets the entire contents of the Env, in key-value pairs.

WARNING: This is not intended for general use, but only for inspecting an Env e.g. for debugging, theme editing, and theme loading.

Adds a key/value, acting like a builder.

Sets a value in an environment.

Panics

Panics if the environment already has a value for the key, but it is of a different type.

Examples found in repository
druid/examples/event_viewer.rs (line 341)
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
pub fn main() {
    //describe the main window
    let main_window = WindowDesc::new(build_root_widget())
        .title("Event Viewer")
        .window_size((760.0, 680.0));

    //start the application
    AppLauncher::with_window(main_window)
        .log_to_console()
        .configure_env(|env, _| {
            env.set(theme::UI_FONT, FontDescriptor::default().with_size(12.0));
            env.set(theme::TEXT_COLOR, TEXT_COLOR);
            env.set(theme::WIDGET_PADDING_HORIZONTAL, 2.0);
            env.set(theme::WIDGET_PADDING_VERTICAL, 2.0);
        })
        .launch(AppState {
            text_input: String::new(),
            events: Arc::new(Vec::new()),
        })
        .expect("Failed to launch application");
}
More examples
druid/examples/sub_window.rs (line 333)
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
fn build_root_widget() -> impl Widget<HelloState> {
    let label = EnvScope::new(
        |env, _t| env.set(theme::TEXT_COLOR, env.get(theme::PRIMARY_LIGHT)),
        ControllerHost::new(
            Label::new(|data: &HelloState, _env: &Env| {
                format!("Hello {}! {} ", data.name, data.sub.my_stuff)
            }),
            TooltipController::new("Tips! Are good"),
        ),
    );
    // a textbox that modifies `name`.
    let textbox = TextBox::new()
        .with_placeholder("Who are we greeting?")
        .fix_width(TEXT_BOX_WIDTH)
        .lens(HelloState::sub.then(SubState::my_stuff));

    let button = Button::new("Make sub window")
        .on_click(|ctx, data: &mut SubState, env| {
            let tb = TextBox::new().lens(SubState::my_stuff);
            let drag_thing = Label::new("Drag me").controller(DragWindowController::new());
            let col = Flex::column().with_child(drag_thing).with_child(tb);

            ctx.new_sub_window(
                WindowConfig::default()
                    .show_titlebar(false)
                    .window_size(Size::new(100., 100.))
                    .set_position(Point::new(1000.0, 500.0))
                    .set_level(WindowLevel::AppWindow),
                col,
                data.clone(),
                env.clone(),
            );
        })
        .center()
        .lens(HelloState::sub);

    let check_box =
        ControllerHost::new(Checkbox::new("Closeable?"), CancelClose).lens(HelloState::closeable);
    // arrange the two widgets vertically, with some padding
    let layout = Flex::column()
        .with_child(label)
        .with_flex_child(ScreenThing.lens(Unit::default()).padding(5.), 1.)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(textbox)
        .with_child(button)
        .with_child(check_box);

    // center the two widgets in the available space
    Align::centered(layout)
}
druid/examples/styled_text.rs (line 110)
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
fn ui_builder() -> impl Widget<AppData> {
    let my_painter = Painter::new(|ctx, _, _| {
        let bounds = ctx.size().to_rect();
        if ctx.is_hot() {
            ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
        }

        if ctx.is_active() {
            ctx.stroke(bounds, &Color::WHITE, 2.0);
        }
    });

    // This is druid's default text style.
    // It's set by theme::LABEL_COLOR and theme::UI_FONT
    let label =
        Label::new(|data: &String, _env: &_| format!("Default: {}", data)).lens(AppData::text);

    // The text_color, text_size, and font builder methods can override the
    // defaults provided by the theme by passing in a Key or a concrete value.
    //
    // In this example, text_color receives a Key from the theme, text_size
    // gets a custom key which we set with the env_scope wrapper, and the
    // default font key (theme::FONT_NAME) is overridden in the env_scope
    // wrapper. (Like text_color and text_size, the font can be set using the
    // with_font builder method, but overriding here makes it easy to fall back
    // to the default font)
    let styled_label = Label::new(|data: &AppData, _env: &_| format!("{}", data))
        .with_text_color(theme::PRIMARY_LIGHT)
        .with_font(MY_CUSTOM_FONT)
        .background(my_painter)
        .on_click(|_, data, _| {
            data.size *= 1.1;
        })
        .env_scope(|env: &mut druid::Env, data: &AppData| {
            let new_font = if data.mono {
                FontDescriptor::new(FontFamily::MONOSPACE)
            } else {
                FontDescriptor::new(FontFamily::SYSTEM_UI)
            }
            .with_size(data.size);
            env.set(MY_CUSTOM_FONT, new_font);
        });

    let labels = Scroll::new(
        Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Start)
            .with_child(label)
            .with_default_spacer()
            .with_child(styled_label),
    )
    .expand_height()
    .fix_width(COLUMN_WIDTH);

    let stepper = Stepper::new()
        .with_range(0.0, 100.0)
        .with_step(1.0)
        .with_wraparound(false)
        .lens(AppData::size);

    let stepper_textbox = LensWrap::new(
        Parse::new(TextBox::new()),
        AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
    );

    let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
    let stepper_row = Flex::row()
        .with_child(stepper_textbox)
        .with_child(stepper)
        .with_default_spacer()
        .with_child(mono_checkbox);

    let input = TextBox::multiline()
        .with_placeholder("Your sample text here :)")
        .fix_width(COLUMN_WIDTH)
        .fix_height(140.0)
        .lens(AppData::text);

    Flex::column()
        .main_axis_alignment(MainAxisAlignment::Center)
        .with_default_spacer()
        .with_flex_child(labels, 1.0)
        .with_default_spacer()
        .with_child(input)
        .with_default_spacer()
        .with_child(stepper_row)
        .with_default_spacer()
}

Try to set a resolved Value for this key.

This will return a ValueTypeError if the value’s inner type differs from the type of the key.

Returns an empty Env.

This is useful for creating a set of overrides.

Trait Implementations

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

Determine whether two values are the same. Read more

Formats the value using the given formatter. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Performs the conversion.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more

Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Performs the conversion.

Performs the conversion.

Performs the conversion.

Should always be Self

The resulting type after obtaining ownership.

Creates owned data from borrowed data, usually by cloning. Read more

🔬 This is a nightly-only experimental API. (toowned_clone_into)

recently added

Uses borrowed data to replace owned data, usually by cloning. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more