Trait druid::widget::WidgetExt

source ·
pub trait WidgetExt<T: Data>: Widget<T> + Sized + 'static {
Show 28 methods // Provided methods fn padding(self, insets: impl Into<KeyOrValue<Insets>>) -> Padding<T, Self> { ... } fn center(self) -> Align<T> { ... } fn align_left(self) -> Align<T> { ... } fn align_right(self) -> Align<T> { ... } fn align_vertical(self, align: UnitPoint) -> Align<T> { ... } fn align_horizontal(self, align: UnitPoint) -> Align<T> { ... } fn fix_width(self, width: impl Into<KeyOrValue<f64>>) -> SizedBox<T> { ... } fn fix_height(self, height: impl Into<KeyOrValue<f64>>) -> SizedBox<T> { ... } fn fix_size( self, width: impl Into<KeyOrValue<f64>>, height: impl Into<KeyOrValue<f64>> ) -> SizedBox<T> { ... } fn expand(self) -> SizedBox<T> { ... } fn expand_width(self) -> SizedBox<T> { ... } fn expand_height(self) -> SizedBox<T> { ... } fn background(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T> { ... } fn foreground(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T> { ... } fn border( self, color: impl Into<KeyOrValue<Color>>, width: impl Into<KeyOrValue<f64>> ) -> Container<T> { ... } fn env_scope(self, f: impl Fn(&mut Env, &T) + 'static) -> EnvScope<T, Self> { ... } fn controller<C: Controller<T, Self>>( self, controller: C ) -> ControllerHost<Self, C> { ... } fn on_added( self, f: impl Fn(&mut Self, &mut LifeCycleCtx<'_, '_>, &T, &Env) + 'static ) -> ControllerHost<Self, Added<T, Self>> { ... } fn on_click( self, f: impl Fn(&mut EventCtx<'_, '_>, &mut T, &Env) + 'static ) -> ControllerHost<Self, Click<T>> { ... } fn debug_paint_layout(self) -> EnvScope<T, Self> { ... } fn debug_widget_id(self) -> EnvScope<T, Self> { ... } fn debug_invalidation(self) -> DebugInvalidation<T, Self> { ... } fn debug_widget(self) -> EnvScope<T, Self> { ... } fn lens<S: Data, L: Lens<S, T>>(self, lens: L) -> LensWrap<S, T, L, Self> { ... } fn with_id(self, id: WidgetId) -> IdentityWrapper<Self> { ... } fn boxed(self) -> Box<dyn Widget<T>> { ... } fn scroll(self) -> Scroll<T, Self> { ... } fn disabled_if( self, disabled_if: impl Fn(&T, &Env) -> bool + 'static ) -> DisabledIf<T, Self> { ... }
}
Expand description

A trait that provides extra methods for combining Widgets.

Provided Methods§

source

fn padding(self, insets: impl Into<KeyOrValue<Insets>>) -> Padding<T, Self>

Wrap this widget in a Padding widget with the given Insets.

Like Padding::new, this can accept a variety of arguments, including a Key referring to Insets in the Env.

Examples found in repository?
examples/async_event.rs (line 88)
81
82
83
84
85
86
87
88
89
90
fn make_ui() -> impl Widget<Color> {
    Painter::new(|ctx, data, _env| {
        let rect = ctx.size().to_rounded_rect(5.0);
        ctx.fill(rect, data);
    })
    .fix_width(300.0)
    .fix_height(300.0)
    .padding(10.0)
    .center()
}
More examples
Hide additional examples
examples/invalidation.rs (line 51)
49
50
51
52
53
54
55
56
fn build_widget() -> impl Widget<AppState> {
    let mut col = Flex::column();
    col.add_child(TextBox::new().lens(AppState::label).padding(3.0));
    for i in 0..30 {
        col.add_child(Button::new(format!("Button {i}")).padding(3.0));
    }
    Split::columns(Scroll::new(col), CircleView.lens(AppState::circles)).debug_invalidation()
}
examples/markdown_preview.rs (line 122)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .with_line_break_mode(LineBreaking::WordWrap)
            .lens(AppState::rendered)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE)),
    )
    .vertical()
    .background(Color::grey8(222))
    .expand();

    let textbox = TextBox::multiline()
        .lens(AppState::raw)
        .controller(RichTextRebuilder)
        .expand()
        .padding(5.0);

    Split::columns(label, textbox)
}
examples/either.rs (line 36)
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
fn ui_builder() -> impl Widget<AppState> {
    // Our UI consists of a column with a button and an `Either` widget
    let button = Checkbox::new("Toggle slider")
        .lens(AppState::which)
        .padding(5.0);

    // The `Either` widget has two children, only one of which is visible at a time.
    // To determine which child is visible, you pass it a closure that takes the
    // `Data` and the `Env` and returns a bool; if it returns `true`, the first
    // widget will be visible, and if `false`, the second.
    let either = Either::new(
        |data, _env| data.which,
        Slider::new().lens(AppState::value).padding(5.0),
        Label::new("Click to reveal slider").padding(5.0),
    );
    Flex::column().with_child(button).with_child(either)
}
examples/textbox.rs (line 70)
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
fn build_root_widget() -> impl Widget<AppState> {
    let blurb = Label::new(EXPLAINER)
        .with_line_break_mode(druid::widget::LineBreaking::WordWrap)
        .padding(8.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0);

    Flex::column()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(blurb)
        .with_spacer(24.0)
        .with_child(
            TextBox::new()
                .with_placeholder("Single")
                .lens(AppState::single),
        )
        .with_default_spacer()
        .with_flex_child(
            TextBox::multiline()
                .with_placeholder("Multi")
                .lens(AppState::multi)
                .expand_width(),
            1.0,
        )
        .padding(8.0)
}
examples/event_viewer.rs (line 260)
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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
fn interactive_area() -> impl Widget<AppState> {
    let text_box = TextBox::multiline()
        .with_text_color(Color::rgb8(0xf0, 0xf0, 0xea))
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .lens(AppState::text_input)
        .controller(EventLogger {
            filter: |event| matches!(event, Event::KeyDown(_) | Event::KeyUp(_)),
        });

    let mouse_box = SizedBox::empty()
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .background(CURSOR_BACKGROUND_COLOR)
        .rounded(5.0)
        .border(INTERACTIVE_AREA_BORDER, 1.0)
        .controller(EventLogger {
            filter: |event| {
                matches!(
                    event,
                    Event::MouseDown(_) | Event::MouseUp(_) | Event::Wheel(_)
                )
            },
        });

    Flex::row()
        .with_flex_spacer(1.0)
        .with_child(text_box)
        .with_flex_spacer(1.0)
        .with_child(mouse_box)
        .with_flex_spacer(1.0)
        .padding(10.0)
}

/// The bottom part of the application, a list of received events.
fn event_list() -> impl Widget<AppState> {
    // Because this would be a HUGE block of repeated code with constants
    // we just use a loop to generate the header.
    let mut header = Flex::row().with_child(
        Label::new(PROPERTIES[0].0)
            .fix_width(PROPERTIES[0].1)
            .background(HEADER_BACKGROUND),
    );

    for (name, size) in PROPERTIES.iter().skip(1) {
        // Keep in mind that later on, in the main function,
        // we set the default spacer values. Without explicitly
        // setting them the default spacer is bigger, and is
        // probably not desirable for your purposes.
        header.add_default_spacer();
        header.add_child(
            Label::new(*name)
                .fix_width(*size)
                .background(HEADER_BACKGROUND),
        );
    }
    Scroll::new(
        Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Start)
            .with_child(header)
            .with_default_spacer()
            .with_flex_child(
                // `List::new` generates a list entry for every element in the `Vec`.
                // In this case it shows a log entry for every element in `AppState::events`.
                // `make_list_item` generates this new log entry.
                Scroll::new(List::new(make_list_item).lens(AppState::events)).vertical(),
                1.0,
            )
            .background(Color::WHITE),
    )
    .horizontal()
    .padding(10.0)
}
source

fn center(self) -> Align<T>

Wrap this widget in an Align widget, configured to center it.

Examples found in repository?
examples/async_event.rs (line 89)
81
82
83
84
85
86
87
88
89
90
fn make_ui() -> impl Widget<Color> {
    Painter::new(|ctx, data, _env| {
        let rect = ctx.size().to_rounded_rect(5.0);
        ctx.fill(rect, data);
    })
    .fix_width(300.0)
    .fix_height(300.0)
    .padding(10.0)
    .center()
}
More examples
Hide additional examples
examples/flex.rs (line 319)
314
315
316
317
318
319
320
321
fn make_ui() -> impl Widget<AppState> {
    Flex::column()
        .must_fill_main_axis(true)
        .with_child(make_control_row())
        .with_default_spacer()
        .with_flex_child(Rebuilder::new().center(), 1.0)
        .padding(10.0)
}
examples/svg.rs (line 49)
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
fn ui_builder() -> impl Widget<u32> {
    let tiger_svg = match include_str!("./assets/tiger.svg").parse::<SvgData>() {
        Ok(svg) => svg,
        Err(err) => {
            error!("{}", err);
            error!("Using an empty SVG instead.");
            SvgData::default()
        }
    };

    let mut col = Flex::column();

    col.add_flex_child(Svg::new(tiger_svg.clone()).fix_width(60.0).center(), 1.0);
    col.add_flex_child(Svg::new(tiger_svg.clone()), 1.0);
    col.add_flex_child(Svg::new(tiger_svg), 1.0);
    col.debug_paint_layout()
}
examples/calc.rs (line 138)
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))
}
examples/lens.rs (line 66)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
fn ui_builder() -> impl Widget<MyComplexState> {
    // `TextBox` is of type `Widget<String>`
    // via `.lens` we get it to be of type `Widget<MyComplexState>`
    let searchbar = TextBox::new().lens(MyComplexState::term_lens);

    // `Slider` is of type `Widget<f64>`
    // via `.lens` we get it to be of type `Widget<MyComplexState>`
    let slider = Slider::new().lens(MyComplexState::scale);

    let label = Label::new(|d: &MyComplexState, _: &Env| format!("{}: {:.2}", d.term, d.scale));

    Flex::column()
        .cross_axis_alignment(CrossAxisAlignment::Center)
        .with_child(label)
        .with_default_spacer()
        .with_child(
            Flex::row()
                .cross_axis_alignment(CrossAxisAlignment::Center)
                .with_child(searchbar)
                .with_default_spacer()
                .with_child(slider),
        )
        .center()
}
examples/blocking_function.rs (line 58)
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
fn ui_builder() -> impl Widget<AppState> {
    let button = Button::new("Start slow increment")
        .on_click(|ctx, data: &mut AppState, _env| {
            data.processing = true;
            // In order to make sure that the other thread can communicate with the main thread we
            // have to pass an external handle to the second thread.
            // Using this handle we can send commands back to the main thread.
            wrapped_slow_function(ctx.get_external_handle(), data.value);
        })
        .padding(5.0);

    let button_placeholder = Flex::column()
        .with_child(Label::new(LocalizedString::new("Processing...")).padding(5.0))
        .with_child(Spinner::new());

    // Hello-counter is defined in the built-in localisation file. This maps to "Current value is {count}"
    // localised in english, french, or german. Every time the value is updated it shows the new value.
    let text = LocalizedString::new("hello-counter")
        .with_arg("count", |data: &AppState, _env| (data.value).into());
    let label = Label::new(text).padding(5.0).center();

    let either = Either::new(|data, _env| data.processing, button_placeholder, button);

    Flex::column().with_child(label).with_child(either)
}
source

fn align_left(self) -> Align<T>

Wrap this widget in an Align widget, configured to align left.

source

fn align_right(self) -> Align<T>

Wrap this widget in an Align widget, configured to align right.

source

fn align_vertical(self, align: UnitPoint) -> Align<T>

Wrap this widget in an Align widget, configured to align vertically.

Examples found in repository?
examples/hello.rs (line 74)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
fn build_root_widget() -> impl Widget<HelloState> {
    // a label that will determine its text based on the current app data.
    let label = Label::new(|data: &HelloState, _env: &Env| {
        if data.name.is_empty() {
            "Hello anybody!?".to_string()
        } else {
            format!("Hello {}!", data.name)
        }
    })
    .with_text_size(32.0);

    // a textbox that modifies `name`.
    let textbox = TextBox::new()
        .with_placeholder("Who are we greeting?")
        .with_text_size(18.0)
        .fix_width(TEXT_BOX_WIDTH)
        .lens(HelloState::name);

    // arrange the two widgets vertically, with some padding
    Flex::column()
        .with_child(label)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(textbox)
        .align_vertical(UnitPoint::CENTER)
}
More examples
Hide additional examples
examples/slider.rs (line 97)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
fn build_root_widget() -> impl Widget<AppState> {
    let range = Flex::row()
        .with_child(Label::dynamic(|value: &(f64, f64), _| {
            format!("Value Range: {value:?}")
        }))
        .with_default_spacer()
        .with_child(
            RangeSlider::new()
                .with_range(0.0, 20.0)
                .with_step(1.0)
                .track_color(KeyOrValue::Concrete(Color::RED))
                .fix_width(250.0),
        )
        .lens(AppState::range);

    let value = Flex::row()
        .with_child(Label::dynamic(|value: &AppState, _| {
            format!("Value: {:?}", value.value)
        }))
        .with_default_spacer()
        .with_child(ViewSwitcher::new(
            |data: &AppState, _| data.range,
            |range, _, _| {
                Slider::new()
                    .with_range(range.0, range.1)
                    .track_color(KeyOrValue::Concrete(Color::RED))
                    .knob_style(KnobStyle::Wedge)
                    .axis(Axis::Vertical)
                    .with_step(0.25)
                    .annotated(1.0, 0.25)
                    .fix_height(250.0)
                    .lens(AppState::value)
                    .boxed()
            },
        ));

    // arrange the two widgets vertically, with some padding
    Flex::column()
        .with_child(range)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(value)
        .cross_axis_alignment(CrossAxisAlignment::End)
        .align_vertical(UnitPoint::RIGHT)
        .padding(20.0)
}
examples/list.rs (line 78)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
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
                data.l_index += 1;
                data.left.push_back(data.l_index as u32);

                // Add child to right list
                data.r_index += 1;
                data.right.push_back(data.r_index as u32);
            })
            .fix_height(30.0)
            .expand_width(),
    );

    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 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}")
                        })
                        .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)
            })
            .with_spacing(10.),
        )
        .vertical()
        .lens(lens::Identity.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.with_child(Label::new("horizontal list"))
        .with_child(
            Scroll::new(
                List::new(|| {
                    Label::new(|item: &u32, _env: &_| format!("List item #{item}"))
                        .padding(10.0)
                        .background(Color::rgb(0.5, 0.5, 0.0))
                        .fix_height(50.0)
                })
                .horizontal()
                .with_spacing(10.)
                .lens(AppData::left),
            )
            .horizontal(),
        )
        .debug_paint_layout()
}
source

fn align_horizontal(self, align: UnitPoint) -> Align<T>

Wrap this widget in an Align widget, configured to align horizontally.

Examples found in repository?
examples/disabled.rs (line 117)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
fn main_widget() -> impl Widget<AppData> {
    Flex::column()
        .with_child(named_child("text:", TextBox::new().lens(AppData::text)))
        .with_default_spacer()
        .with_child(
            named_child("text (disabled):", TextBox::new().lens(AppData::text))
                .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(named_child("text:", TextBox::new().lens(AppData::text)))
        .with_default_spacer()
        .with_child(
            named_child("text (disabled):", TextBox::new().lens(AppData::text))
                .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_default_spacer()
        .with_child(
            named_child(
                "value (disabled):",
                Slider::new().with_range(0.0, 10.0).lens(AppData::value),
            )
            .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            named_child(
                "value (disabled):",
                Stepper::new()
                    .with_range(0.0, 10.0)
                    .with_step(0.5)
                    .lens(AppData::value),
            )
            .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            named_child(
                "option (disabled):",
                Checkbox::new("option").lens(AppData::option),
            )
            .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            named_child("option (disabled):", Switch::new().lens(AppData::option))
                .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            Flex::row()
                .with_child(
                    Button::new("-")
                        .on_click(|_, data: &mut f64, _| *data -= 1.0)
                        .disabled_if(|data, _| *data < 1.0),
                )
                .with_default_spacer()
                .with_child(Label::dynamic(|data: &f64, _| data.to_string()))
                .with_default_spacer()
                .with_child(
                    Button::new("+")
                        .on_click(|_, data: &mut f64, _| *data += 1.0)
                        .disabled_if(|data, _| *data > 9.0),
                )
                .lens(AppData::value)
                .disabled_if(|data: &AppData, _| data.disabled),
        )
        .with_default_spacer()
        .with_default_spacer()
        .with_default_spacer()
        .with_child(Checkbox::new("disabled").lens(AppData::disabled))
        .with_default_spacer()
        .cross_axis_alignment(CrossAxisAlignment::End)
        .align_horizontal(UnitPoint::CENTER)
}
source

fn fix_width(self, width: impl Into<KeyOrValue<f64>>) -> SizedBox<T>

Wrap this widget in a SizedBox with an explicit width.

Examples found in repository?
examples/async_event.rs (line 86)
81
82
83
84
85
86
87
88
89
90
fn make_ui() -> impl Widget<Color> {
    Painter::new(|ctx, data, _env| {
        let rect = ctx.size().to_rounded_rect(5.0);
        ctx.fill(rect, data);
    })
    .fix_width(300.0)
    .fix_height(300.0)
    .padding(10.0)
    .center()
}
More examples
Hide additional examples
examples/svg.rs (line 49)
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
fn ui_builder() -> impl Widget<u32> {
    let tiger_svg = match include_str!("./assets/tiger.svg").parse::<SvgData>() {
        Ok(svg) => svg,
        Err(err) => {
            error!("{}", err);
            error!("Using an empty SVG instead.");
            SvgData::default()
        }
    };

    let mut col = Flex::column();

    col.add_flex_child(Svg::new(tiger_svg.clone()).fix_width(60.0).center(), 1.0);
    col.add_flex_child(Svg::new(tiger_svg.clone()), 1.0);
    col.add_flex_child(Svg::new(tiger_svg), 1.0);
    col.debug_paint_layout()
}
examples/hello.rs (line 66)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
fn build_root_widget() -> impl Widget<HelloState> {
    // a label that will determine its text based on the current app data.
    let label = Label::new(|data: &HelloState, _env: &Env| {
        if data.name.is_empty() {
            "Hello anybody!?".to_string()
        } else {
            format!("Hello {}!", data.name)
        }
    })
    .with_text_size(32.0);

    // a textbox that modifies `name`.
    let textbox = TextBox::new()
        .with_placeholder("Who are we greeting?")
        .with_text_size(18.0)
        .fix_width(TEXT_BOX_WIDTH)
        .lens(HelloState::name);

    // arrange the two widgets vertically, with some padding
    Flex::column()
        .with_child(label)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(textbox)
        .align_vertical(UnitPoint::CENTER)
}
examples/flex.rs (line 220)
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
fn make_spacer_select() -> impl Widget<Params> {
    Flex::column()
        .cross_axis_alignment(CrossAxisAlignment::Start)
        .with_child(Label::new("Insert Spacers:"))
        .with_default_spacer()
        .with_child(RadioGroup::column(SPACER_OPTIONS.to_vec()).lens(Params::spacers))
        .with_default_spacer()
        .with_child(
            Flex::row()
                .with_child(
                    TextBox::new()
                        .with_formatter(ParseFormatter::new())
                        .lens(Params::spacer_size)
                        .fix_width(60.0),
                )
                .with_spacer(druid::theme::WIDGET_CONTROL_COMPONENT_PADDING)
                .with_child(
                    Stepper::new()
                        .with_range(2.0, 50.0)
                        .with_step(2.0)
                        .lens(Params::spacer_size),
                ),
        )
}
examples/event_viewer.rs (line 269)
264
265
266
267
268
269
270
271
272
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
fn event_list() -> impl Widget<AppState> {
    // Because this would be a HUGE block of repeated code with constants
    // we just use a loop to generate the header.
    let mut header = Flex::row().with_child(
        Label::new(PROPERTIES[0].0)
            .fix_width(PROPERTIES[0].1)
            .background(HEADER_BACKGROUND),
    );

    for (name, size) in PROPERTIES.iter().skip(1) {
        // Keep in mind that later on, in the main function,
        // we set the default spacer values. Without explicitly
        // setting them the default spacer is bigger, and is
        // probably not desirable for your purposes.
        header.add_default_spacer();
        header.add_child(
            Label::new(*name)
                .fix_width(*size)
                .background(HEADER_BACKGROUND),
        );
    }
    Scroll::new(
        Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Start)
            .with_child(header)
            .with_default_spacer()
            .with_flex_child(
                // `List::new` generates a list entry for every element in the `Vec`.
                // In this case it shows a log entry for every element in `AppState::events`.
                // `make_list_item` generates this new log entry.
                Scroll::new(List::new(make_list_item).lens(AppState::events)).vertical(),
                1.0,
            )
            .background(Color::WHITE),
    )
    .horizontal()
    .padding(10.0)
}

/// A single event row.
fn make_list_item() -> impl Widget<LoggedEvent> {
    Flex::row()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.number()).fix_width(PROPERTIES[0].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.name()).fix_width(PROPERTIES[1].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.mouse_pos()).fix_width(PROPERTIES[2].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.wheel_delta()).fix_width(PROPERTIES[3].1))
        .with_default_spacer()
        .with_child(
            Label::dynamic(|d: &LoggedEvent, _| d.mouse_button()).fix_width(PROPERTIES[4].1),
        )
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.click_count()).fix_width(PROPERTIES[5].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.is_repeat()).fix_width(PROPERTIES[6].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.key()).fix_width(PROPERTIES[7].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.code()).fix_width(PROPERTIES[8].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.modifiers()).fix_width(PROPERTIES[9].1))
        .with_default_spacer()
        .with_child(Label::dynamic(|d: &LoggedEvent, _| d.location()).fix_width(PROPERTIES[10].1))
}
examples/slider.rs (line 66)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
fn build_root_widget() -> impl Widget<AppState> {
    let range = Flex::row()
        .with_child(Label::dynamic(|value: &(f64, f64), _| {
            format!("Value Range: {value:?}")
        }))
        .with_default_spacer()
        .with_child(
            RangeSlider::new()
                .with_range(0.0, 20.0)
                .with_step(1.0)
                .track_color(KeyOrValue::Concrete(Color::RED))
                .fix_width(250.0),
        )
        .lens(AppState::range);

    let value = Flex::row()
        .with_child(Label::dynamic(|value: &AppState, _| {
            format!("Value: {:?}", value.value)
        }))
        .with_default_spacer()
        .with_child(ViewSwitcher::new(
            |data: &AppState, _| data.range,
            |range, _, _| {
                Slider::new()
                    .with_range(range.0, range.1)
                    .track_color(KeyOrValue::Concrete(Color::RED))
                    .knob_style(KnobStyle::Wedge)
                    .axis(Axis::Vertical)
                    .with_step(0.25)
                    .annotated(1.0, 0.25)
                    .fix_height(250.0)
                    .lens(AppState::value)
                    .boxed()
            },
        ));

    // arrange the two widgets vertically, with some padding
    Flex::column()
        .with_child(range)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(value)
        .cross_axis_alignment(CrossAxisAlignment::End)
        .align_vertical(UnitPoint::RIGHT)
        .padding(20.0)
}
source

fn fix_height(self, height: impl Into<KeyOrValue<f64>>) -> SizedBox<T>

Wrap this widget in a SizedBox with an explicit height.

Examples found in repository?
examples/slider.rs (line 85)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
fn build_root_widget() -> impl Widget<AppState> {
    let range = Flex::row()
        .with_child(Label::dynamic(|value: &(f64, f64), _| {
            format!("Value Range: {value:?}")
        }))
        .with_default_spacer()
        .with_child(
            RangeSlider::new()
                .with_range(0.0, 20.0)
                .with_step(1.0)
                .track_color(KeyOrValue::Concrete(Color::RED))
                .fix_width(250.0),
        )
        .lens(AppState::range);

    let value = Flex::row()
        .with_child(Label::dynamic(|value: &AppState, _| {
            format!("Value: {:?}", value.value)
        }))
        .with_default_spacer()
        .with_child(ViewSwitcher::new(
            |data: &AppState, _| data.range,
            |range, _, _| {
                Slider::new()
                    .with_range(range.0, range.1)
                    .track_color(KeyOrValue::Concrete(Color::RED))
                    .knob_style(KnobStyle::Wedge)
                    .axis(Axis::Vertical)
                    .with_step(0.25)
                    .annotated(1.0, 0.25)
                    .fix_height(250.0)
                    .lens(AppState::value)
                    .boxed()
            },
        ));

    // arrange the two widgets vertically, with some padding
    Flex::column()
        .with_child(range)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(value)
        .cross_axis_alignment(CrossAxisAlignment::End)
        .align_vertical(UnitPoint::RIGHT)
        .padding(20.0)
}
More examples
Hide additional examples
examples/layout.rs (line 51)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
fn build_app() -> impl Widget<u32> {
    // Usually we put all the widgets in one big tree using builder-style
    // methods. Sometimes we split them up in declarations to increase
    // readability. In this case we also have some recurring elements,
    // we add those in a loop later on.
    let mut col = Flex::column().with_child(
        // The `Flex`'s first child is another Flex! In this case it is
        // a row.
        Flex::row()
            // The row has its own children.
            .with_child(
                Label::new("One")
                    .fix_width(60.0)
                    .background(Color::rgb8(0x77, 0x77, 0))
                    .border(Color::WHITE, 3.0)
                    .center(),
            )
            // Spacing element that will fill all available space in
            // between label and a button. Notice that weight is non-zero.
            // We could have achieved a similar result with expanding the
            // width and setting the main-axis-allignment to SpaceBetween.
            .with_flex_spacer(1.0)
            .with_child(Button::new("Two").padding(20.))
            // After we added all the children, we can set some more
            // values using builder-style methods. Since these methods
            // dont return the original `Flex` but a SizedBox and Container
            // respectively, we have to put these at the end.
            .fix_height(100.0)
            //turquoise
            .background(Color::rgb8(0, 0x77, 0x88)),
    );

    for i in 0..5 {
        // Give a larger weight to one of the buttons for it to
        // occupy more space.
        let weight = if i == 2 { 3.0 } else { 1.0 };
        // call `expand_height` to force the buttons to use all their provided flex
        col.add_flex_child(Button::new(format!("Button #{i}")).expand_height(), weight);
    }

    // aspect ratio box
    let aspect_ratio_label = Label::new("This is an aspect-ratio box. Notice how the text will overflow if the box becomes too small.")
        .with_text_color(Color::BLACK)
        .with_line_break_mode(LineBreaking::WordWrap)
        .center();
    let aspect_ratio_box = AspectRatioBox::new(aspect_ratio_label, 4.0)
        .border(Color::BLACK, 1.0)
        .background(Color::WHITE);
    col.add_flex_child(aspect_ratio_box.center(), 1.0);

    // This method asks Druid to draw colored rectangles around our widgets,
    // so we can visually inspect their layout rectangles.
    col.debug_paint_layout()
}
examples/list.rs (line 68)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
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
                data.l_index += 1;
                data.left.push_back(data.l_index as u32);

                // Add child to right list
                data.r_index += 1;
                data.right.push_back(data.r_index as u32);
            })
            .fix_height(30.0)
            .expand_width(),
    );

    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 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}")
                        })
                        .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)
            })
            .with_spacing(10.),
        )
        .vertical()
        .lens(lens::Identity.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.with_child(Label::new("horizontal list"))
        .with_child(
            Scroll::new(
                List::new(|| {
                    Label::new(|item: &u32, _env: &_| format!("List item #{item}"))
                        .padding(10.0)
                        .background(Color::rgb(0.5, 0.5, 0.0))
                        .fix_height(50.0)
                })
                .horizontal()
                .with_spacing(10.)
                .lens(AppData::left),
            )
            .horizontal(),
        )
        .debug_paint_layout()
}
source

fn fix_size( self, width: impl Into<KeyOrValue<f64>>, height: impl Into<KeyOrValue<f64>> ) -> SizedBox<T>

Wrap this widget in an SizedBox with an explicit width and height

Examples found in repository?
examples/event_viewer.rs (line 234)
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
fn interactive_area() -> impl Widget<AppState> {
    let text_box = TextBox::multiline()
        .with_text_color(Color::rgb8(0xf0, 0xf0, 0xea))
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .lens(AppState::text_input)
        .controller(EventLogger {
            filter: |event| matches!(event, Event::KeyDown(_) | Event::KeyUp(_)),
        });

    let mouse_box = SizedBox::empty()
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .background(CURSOR_BACKGROUND_COLOR)
        .rounded(5.0)
        .border(INTERACTIVE_AREA_BORDER, 1.0)
        .controller(EventLogger {
            filter: |event| {
                matches!(
                    event,
                    Event::MouseDown(_) | Event::MouseUp(_) | Event::Wheel(_)
                )
            },
        });

    Flex::row()
        .with_flex_spacer(1.0)
        .with_child(text_box)
        .with_flex_spacer(1.0)
        .with_child(mouse_box)
        .with_flex_spacer(1.0)
        .padding(10.0)
}
More examples
Hide additional examples
examples/list.rs (line 108)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
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
                data.l_index += 1;
                data.left.push_back(data.l_index as u32);

                // Add child to right list
                data.r_index += 1;
                data.right.push_back(data.r_index as u32);
            })
            .fix_height(30.0)
            .expand_width(),
    );

    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 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}")
                        })
                        .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)
            })
            .with_spacing(10.),
        )
        .vertical()
        .lens(lens::Identity.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.with_child(Label::new("horizontal list"))
        .with_child(
            Scroll::new(
                List::new(|| {
                    Label::new(|item: &u32, _env: &_| format!("List item #{item}"))
                        .padding(10.0)
                        .background(Color::rgb(0.5, 0.5, 0.0))
                        .fix_height(50.0)
                })
                .horizontal()
                .with_spacing(10.)
                .lens(AppData::left),
            )
            .horizontal(),
        )
        .debug_paint_layout()
}
source

fn expand(self) -> SizedBox<T>

Wrap this widget in a SizedBox with an infinite width and height.

Only call this method if you want your widget to occupy all available space. If you only care about expanding in one of width or height, use expand_width or expand_height instead.

Examples found in repository?
examples/markdown_preview.rs (line 126)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .with_line_break_mode(LineBreaking::WordWrap)
            .lens(AppState::rendered)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE)),
    )
    .vertical()
    .background(Color::grey8(222))
    .expand();

    let textbox = TextBox::multiline()
        .lens(AppState::raw)
        .controller(RichTextRebuilder)
        .expand()
        .padding(5.0);

    Split::columns(label, textbox)
}
More examples
Hide additional examples
examples/calc.rs (line 140)
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))
}
examples/transparency.rs (line 101)
62
63
64
65
66
67
68
69
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
fn build_root_widget() -> impl Widget<HelloState> {
    // Draw red circle, and two semi-transparent rectangles
    let circle_and_rects = Painter::new(|ctx, _data, _env| {
        let boundaries = ctx.size().to_rect();
        let center = (boundaries.width() / 2., boundaries.height() / 2.);
        let circle = Circle::new(center, center.0.min(center.1));
        ctx.fill(circle, &Color::RED);

        let rect1 = Rect::new(0., 0., boundaries.width() / 2., boundaries.height() / 2.);
        ctx.fill(rect1, &Color::rgba8(0x0, 0xff, 0, 125));

        let rect2 = Rect::new(
            boundaries.width() / 2.,
            boundaries.height() / 2.,
            boundaries.width(),
            boundaries.height(),
        );
        ctx.fill(rect2, &Color::rgba8(0x0, 0x0, 0xff, 125));
    });

    // This textbox modifies the label, idea here is to test that the background
    // invalidation works when you type to the textbox
    let textbox = TextBox::new()
        .with_placeholder("Type to test clearing")
        .with_text_size(18.0)
        .lens(HelloState::name)
        .fix_width(250.);

    let label = Label::new(|data: &HelloState, _env: &Env| {
        if data.name.is_empty() {
            "Text: ".to_string()
        } else {
            format!("Text: {}!", data.name)
        }
    })
    .with_text_color(Color::RED)
    .with_text_size(32.0);

    Flex::column()
        .with_flex_child(circle_and_rects.expand().controller(DragController), 10.0)
        .with_spacer(4.0)
        .with_child(textbox)
        .with_spacer(4.0)
        .with_child(label)
}
examples/list.rs (line 80)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
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
                data.l_index += 1;
                data.left.push_back(data.l_index as u32);

                // Add child to right list
                data.r_index += 1;
                data.right.push_back(data.r_index as u32);
            })
            .fix_height(30.0)
            .expand_width(),
    );

    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 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}")
                        })
                        .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)
            })
            .with_spacing(10.),
        )
        .vertical()
        .lens(lens::Identity.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.with_child(Label::new("horizontal list"))
        .with_child(
            Scroll::new(
                List::new(|| {
                    Label::new(|item: &u32, _env: &_| format!("List item #{item}"))
                        .padding(10.0)
                        .background(Color::rgb(0.5, 0.5, 0.0))
                        .fix_height(50.0)
                })
                .horizontal()
                .with_spacing(10.)
                .lens(AppData::left),
            )
            .horizontal(),
        )
        .debug_paint_layout()
}
source

fn expand_width(self) -> SizedBox<T>

Wrap this widget in a SizedBox with an infinite width.

This will force the child to use all available space on the x-axis.

Examples found in repository?
examples/tabs.rs (line 109)
103
104
105
106
107
108
109
110
111
112
113
114
115
    fn group<T: Data, W: Widget<T> + 'static>(text: &str, w: W) -> impl Widget<T> {
        Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Start)
            .with_child(
                Label::new(text)
                    .background(theme::PLACEHOLDER_COLOR)
                    .expand_width(),
            )
            .with_default_spacer()
            .with_child(w)
            .with_default_spacer()
            .border(Color::WHITE, 0.5)
    }
More examples
Hide additional examples
examples/markdown_preview.rs (line 121)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .with_line_break_mode(LineBreaking::WordWrap)
            .lens(AppState::rendered)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE)),
    )
    .vertical()
    .background(Color::grey8(222))
    .expand();

    let textbox = TextBox::multiline()
        .lens(AppState::raw)
        .controller(RichTextRebuilder)
        .expand()
        .padding(5.0);

    Split::columns(label, textbox)
}
examples/textbox.rs (line 88)
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
fn build_root_widget() -> impl Widget<AppState> {
    let blurb = Label::new(EXPLAINER)
        .with_line_break_mode(druid::widget::LineBreaking::WordWrap)
        .padding(8.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0);

    Flex::column()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(blurb)
        .with_spacer(24.0)
        .with_child(
            TextBox::new()
                .with_placeholder("Single")
                .lens(AppState::single),
        )
        .with_default_spacer()
        .with_flex_child(
            TextBox::multiline()
                .with_placeholder("Multi")
                .lens(AppState::multi)
                .expand_width(),
            1.0,
        )
        .padding(8.0)
}
examples/text.rs (line 117)
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
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .controller(LabelController)
            .background(Color::WHITE)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE))
            .background(Color::grey8(222)),
    )
    .vertical();

    let line_break_chooser = Flex::column()
        .with_child(Label::new("Line break mode"))
        .with_spacer(SPACER_SIZE)
        .with_child(RadioGroup::column(vec![
            ("Clip", LineBreaking::Clip),
            ("Wrap", LineBreaking::WordWrap),
            ("Overflow", LineBreaking::Overflow),
        ]))
        .lens(AppState::line_break_mode);

    let alignment_picker = Flex::column()
        .with_child(Label::new("Justification"))
        .with_spacer(SPACER_SIZE)
        .with_child(RadioGroup::column(vec![
            ("Start", TextAlignment::Start),
            ("End", TextAlignment::End),
            ("Center", TextAlignment::Center),
            ("Justified", TextAlignment::Justified),
        ]))
        .lens(AppState::alignment);

    let controls = Flex::row()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(alignment_picker)
        .with_spacer(SPACER_SIZE)
        .with_child(line_break_chooser)
        .padding(SPACER_SIZE);

    Flex::column()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(controls)
        .with_flex_child(label, 1.0)
}
examples/game_of_life.rs (line 411)
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
fn make_widget() -> impl Widget<AppData> {
    Flex::column()
        .with_flex_child(
            GameOfLifeWidget {
                timer_id: TimerToken::INVALID,
                cell_size: Size {
                    width: 0.0,
                    height: 0.0,
                },
                last_update: Instant::now(),
            },
            1.0,
        )
        .with_child(
            Flex::column()
                .with_child(
                    // a row with two buttons
                    Flex::row()
                        .with_flex_child(
                            // pause / resume button
                            Button::new(|data: &bool, _: &Env| match data {
                                true => "Resume",
                                false => "Pause",
                            })
                            .on_click(|ctx, data: &mut bool, _: &Env| {
                                *data = !*data;
                                ctx.request_layout();
                            })
                            .lens(AppData::paused)
                            .padding((5., 5.)),
                            1.0,
                        )
                        .with_flex_child(
                            // clear button
                            Button::new("Clear")
                                .on_click(|ctx, data: &mut Grid, _: &Env| {
                                    data.clear();
                                    ctx.request_paint();
                                })
                                .lens(AppData::grid)
                                .padding((5., 5.)),
                            1.0,
                        )
                        .padding(8.0),
                )
                .with_child(
                    Flex::row()
                        .with_child(
                            Label::new(|data: &AppData, _env: &_| {
                                format!("{:.2}updates/s", data.updates_per_second)
                            })
                            .padding(3.0),
                        )
                        .with_flex_child(
                            Slider::new()
                                .with_range(0.2, 20.0)
                                .expand_width()
                                .lens(AppData::updates_per_second),
                            1.,
                        )
                        .padding(8.0),
                )
                .background(BACKGROUND),
        )
}
source

fn expand_height(self) -> SizedBox<T>

Wrap this widget in a SizedBox with an infinite width.

This will force the child to use all available space on the y-axis.

Examples found in repository?
examples/layout.rs (line 61)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
fn build_app() -> impl Widget<u32> {
    // Usually we put all the widgets in one big tree using builder-style
    // methods. Sometimes we split them up in declarations to increase
    // readability. In this case we also have some recurring elements,
    // we add those in a loop later on.
    let mut col = Flex::column().with_child(
        // The `Flex`'s first child is another Flex! In this case it is
        // a row.
        Flex::row()
            // The row has its own children.
            .with_child(
                Label::new("One")
                    .fix_width(60.0)
                    .background(Color::rgb8(0x77, 0x77, 0))
                    .border(Color::WHITE, 3.0)
                    .center(),
            )
            // Spacing element that will fill all available space in
            // between label and a button. Notice that weight is non-zero.
            // We could have achieved a similar result with expanding the
            // width and setting the main-axis-allignment to SpaceBetween.
            .with_flex_spacer(1.0)
            .with_child(Button::new("Two").padding(20.))
            // After we added all the children, we can set some more
            // values using builder-style methods. Since these methods
            // dont return the original `Flex` but a SizedBox and Container
            // respectively, we have to put these at the end.
            .fix_height(100.0)
            //turquoise
            .background(Color::rgb8(0, 0x77, 0x88)),
    );

    for i in 0..5 {
        // Give a larger weight to one of the buttons for it to
        // occupy more space.
        let weight = if i == 2 { 3.0 } else { 1.0 };
        // call `expand_height` to force the buttons to use all their provided flex
        col.add_flex_child(Button::new(format!("Button #{i}")).expand_height(), weight);
    }

    // aspect ratio box
    let aspect_ratio_label = Label::new("This is an aspect-ratio box. Notice how the text will overflow if the box becomes too small.")
        .with_text_color(Color::BLACK)
        .with_line_break_mode(LineBreaking::WordWrap)
        .center();
    let aspect_ratio_box = AspectRatioBox::new(aspect_ratio_label, 4.0)
        .border(Color::BLACK, 1.0)
        .background(Color::WHITE);
    col.add_flex_child(aspect_ratio_box.center(), 1.0);

    // This method asks Druid to draw colored rectangles around our widgets,
    // so we can visually inspect their layout rectangles.
    col.debug_paint_layout()
}
More examples
Hide additional examples
examples/styled_text.rs (line 122)
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
157
158
159
160
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);

    // TODO: Replace Parse usage with TextBox::with_formatter
    #[allow(deprecated)]
    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()
}
source

fn background(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T>

Wrap this widget in a Container with the provided background brush.

See Container::background for more information.

Examples found in repository?
examples/tabs.rs (line 108)
103
104
105
106
107
108
109
110
111
112
113
114
115
    fn group<T: Data, W: Widget<T> + 'static>(text: &str, w: W) -> impl Widget<T> {
        Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Start)
            .with_child(
                Label::new(text)
                    .background(theme::PLACEHOLDER_COLOR)
                    .expand_width(),
            )
            .with_default_spacer()
            .with_child(w)
            .with_default_spacer()
            .border(Color::WHITE, 0.5)
    }
More examples
Hide additional examples
examples/markdown_preview.rs (line 125)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .with_line_break_mode(LineBreaking::WordWrap)
            .lens(AppState::rendered)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE)),
    )
    .vertical()
    .background(Color::grey8(222))
    .expand();

    let textbox = TextBox::multiline()
        .lens(AppState::raw)
        .controller(RichTextRebuilder)
        .expand()
        .padding(5.0);

    Split::columns(label, textbox)
}
examples/calc.rs (line 139)
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))
}
examples/event_viewer.rs (line 242)
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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
fn interactive_area() -> impl Widget<AppState> {
    let text_box = TextBox::multiline()
        .with_text_color(Color::rgb8(0xf0, 0xf0, 0xea))
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .lens(AppState::text_input)
        .controller(EventLogger {
            filter: |event| matches!(event, Event::KeyDown(_) | Event::KeyUp(_)),
        });

    let mouse_box = SizedBox::empty()
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .background(CURSOR_BACKGROUND_COLOR)
        .rounded(5.0)
        .border(INTERACTIVE_AREA_BORDER, 1.0)
        .controller(EventLogger {
            filter: |event| {
                matches!(
                    event,
                    Event::MouseDown(_) | Event::MouseUp(_) | Event::Wheel(_)
                )
            },
        });

    Flex::row()
        .with_flex_spacer(1.0)
        .with_child(text_box)
        .with_flex_spacer(1.0)
        .with_child(mouse_box)
        .with_flex_spacer(1.0)
        .padding(10.0)
}

/// The bottom part of the application, a list of received events.
fn event_list() -> impl Widget<AppState> {
    // Because this would be a HUGE block of repeated code with constants
    // we just use a loop to generate the header.
    let mut header = Flex::row().with_child(
        Label::new(PROPERTIES[0].0)
            .fix_width(PROPERTIES[0].1)
            .background(HEADER_BACKGROUND),
    );

    for (name, size) in PROPERTIES.iter().skip(1) {
        // Keep in mind that later on, in the main function,
        // we set the default spacer values. Without explicitly
        // setting them the default spacer is bigger, and is
        // probably not desirable for your purposes.
        header.add_default_spacer();
        header.add_child(
            Label::new(*name)
                .fix_width(*size)
                .background(HEADER_BACKGROUND),
        );
    }
    Scroll::new(
        Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Start)
            .with_child(header)
            .with_default_spacer()
            .with_flex_child(
                // `List::new` generates a list entry for every element in the `Vec`.
                // In this case it shows a log entry for every element in `AppState::events`.
                // `make_list_item` generates this new log entry.
                Scroll::new(List::new(make_list_item).lens(AppState::events)).vertical(),
                1.0,
            )
            .background(Color::WHITE),
    )
    .horizontal()
    .padding(10.0)
}
examples/text.rs (line 116)
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
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .controller(LabelController)
            .background(Color::WHITE)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE))
            .background(Color::grey8(222)),
    )
    .vertical();

    let line_break_chooser = Flex::column()
        .with_child(Label::new("Line break mode"))
        .with_spacer(SPACER_SIZE)
        .with_child(RadioGroup::column(vec![
            ("Clip", LineBreaking::Clip),
            ("Wrap", LineBreaking::WordWrap),
            ("Overflow", LineBreaking::Overflow),
        ]))
        .lens(AppState::line_break_mode);

    let alignment_picker = Flex::column()
        .with_child(Label::new("Justification"))
        .with_spacer(SPACER_SIZE)
        .with_child(RadioGroup::column(vec![
            ("Start", TextAlignment::Start),
            ("End", TextAlignment::End),
            ("Center", TextAlignment::Center),
            ("Justified", TextAlignment::Justified),
        ]))
        .lens(AppState::alignment);

    let controls = Flex::row()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(alignment_picker)
        .with_spacer(SPACER_SIZE)
        .with_child(line_break_chooser)
        .padding(SPACER_SIZE);

    Flex::column()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(controls)
        .with_flex_child(label, 1.0)
}
examples/input_region.rs (line 52)
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    pub fn new() -> Self {
        let info_label = Label::new(INFO_TEXT)
            .with_line_break_mode(LineBreaking::WordWrap)
            .padding(20.0)
            .background(Color::rgba(0.2, 0.2, 0.2, 1.0));
        let toggle_input_region = Button::new("Toggle Input Region")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting input region toggle to: {}", *data);
                ctx.request_layout();
            })
            .lens(AppState::limit_input_region);
        let toggle_titlebar = Button::new("Toggle TitleBar")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting titlebar visibility to: {}", *data);
                ctx.window().show_titlebar(*data);
                ctx.request_layout();
            })
            .lens(AppState::show_titlebar);
        let toggle_always_on_top = Button::new("Toggle Always On Top")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting always on top to: {}", *data);
                ctx.window().set_always_on_top(*data);
            })
            .lens(AppState::always_on_top);
        let controls_flex = Flex::row()
            .with_child(toggle_input_region)
            .with_child(toggle_titlebar)
            .with_child(toggle_always_on_top);
        Self {
            info_label: WidgetPod::new(info_label),
            controls: WidgetPod::new(controls_flex),
        }
    }
source

fn foreground(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T>

Wrap this widget in a Container with the provided foreground brush.

See Container::foreground for more information.

source

fn border( self, color: impl Into<KeyOrValue<Color>>, width: impl Into<KeyOrValue<f64>> ) -> Container<T>

Wrap this widget in a Container with the given border.

Arguments can be either concrete values, or a Key of the respective type.

Examples found in repository?
examples/tabs.rs (line 114)
103
104
105
106
107
108
109
110
111
112
113
114
115
    fn group<T: Data, W: Widget<T> + 'static>(text: &str, w: W) -> impl Widget<T> {
        Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Start)
            .with_child(
                Label::new(text)
                    .background(theme::PLACEHOLDER_COLOR)
                    .expand_width(),
            )
            .with_default_spacer()
            .with_child(w)
            .with_default_spacer()
            .border(Color::WHITE, 0.5)
    }
More examples
Hide additional examples
examples/textbox.rs (line 71)
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
fn build_root_widget() -> impl Widget<AppState> {
    let blurb = Label::new(EXPLAINER)
        .with_line_break_mode(druid::widget::LineBreaking::WordWrap)
        .padding(8.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0);

    Flex::column()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(blurb)
        .with_spacer(24.0)
        .with_child(
            TextBox::new()
                .with_placeholder("Single")
                .lens(AppState::single),
        )
        .with_default_spacer()
        .with_flex_child(
            TextBox::multiline()
                .with_placeholder("Multi")
                .lens(AppState::multi)
                .expand_width(),
            1.0,
        )
        .padding(8.0)
}
examples/panels.rs (line 61)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
fn build_app() -> impl Widget<()> {
    let gradient = LinearGradient::new(
        UnitPoint::TOP_LEFT,
        UnitPoint::BOTTOM_RIGHT,
        (DARKER_GREY, LIGHTER_GREY),
    );

    // a custom background
    let polka_dots = Painter::new(|ctx, _, _| {
        let bounds = ctx.size().to_rect();
        let dot_diam = bounds.width().max(bounds.height()) / 20.;
        let dot_spacing = dot_diam * 1.8;
        for y in 0..((bounds.height() / dot_diam).ceil() as usize) {
            for x in 0..((bounds.width() / dot_diam).ceil() as usize) {
                let x_offset = (y % 2) as f64 * (dot_spacing / 2.0);
                let x = x as f64 * dot_spacing + x_offset;
                let y = y as f64 * dot_spacing;
                let circ = Circle::new((x, y), dot_diam / 2.0);
                let purp = Color::rgb(1.0, 0.22, 0.76);
                ctx.fill(circ, &purp);
            }
        }
    });

    Flex::column()
        .with_flex_child(
            Flex::row()
                .with_flex_child(
                    Label::new("top left")
                        .center()
                        .border(DARK_GREY, 4.0)
                        .padding(10.0),
                    1.0,
                )
                .with_flex_child(
                    Label::new("top right")
                        .center()
                        .background(DARK_GREY)
                        .padding(10.0),
                    1.0,
                ),
            1.0,
        )
        .with_flex_child(
            Flex::row()
                .with_flex_child(
                    Label::new("bottom left")
                        .center()
                        .background(gradient)
                        .rounded(10.0)
                        .padding(10.0),
                    1.0,
                )
                .with_flex_child(
                    Label::new("bottom right")
                        .center()
                        .border(LIGHTER_GREY, 4.0)
                        .background(polka_dots)
                        .rounded(10.0)
                        .padding(10.0),
                    1.0,
                ),
            1.0,
        )
}
examples/flex.rs (line 202)
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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
fn make_control_row() -> impl Widget<AppState> {
    Flex::row()
        .cross_axis_alignment(CrossAxisAlignment::Start)
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("Type:"))
                .with_default_spacer()
                .with_child(RadioGroup::column(FLEX_TYPE_OPTIONS.to_vec()).lens(Params::axis)),
        )
        .with_default_spacer()
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("CrossAxis:"))
                .with_default_spacer()
                .with_child(
                    RadioGroup::column(CROSS_AXIS_ALIGNMENT_OPTIONS.to_vec())
                        .lens(Params::cross_alignment),
                ),
        )
        .with_default_spacer()
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("MainAxis:"))
                .with_default_spacer()
                .with_child(
                    RadioGroup::column(MAIN_AXIS_ALIGNMENT_OPTIONS.to_vec())
                        .lens(Params::main_alignment),
                ),
        )
        .with_default_spacer()
        .with_child(make_spacer_select())
        .with_default_spacer()
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("Misc:"))
                .with_default_spacer()
                .with_child(Checkbox::new("Debug layout").lens(Params::debug_layout))
                .with_default_spacer()
                .with_child(Checkbox::new("Fill main axis").lens(Params::fill_major_axis))
                .with_default_spacer()
                .with_child(Checkbox::new("Fix minor axis size").lens(Params::fix_minor_axis))
                .with_default_spacer()
                .with_child(Checkbox::new("Fix major axis size").lens(Params::fix_major_axis)),
        )
        .padding(10.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0)
        .lens(AppState::params)
}

fn make_spacer_select() -> impl Widget<Params> {
    Flex::column()
        .cross_axis_alignment(CrossAxisAlignment::Start)
        .with_child(Label::new("Insert Spacers:"))
        .with_default_spacer()
        .with_child(RadioGroup::column(SPACER_OPTIONS.to_vec()).lens(Params::spacers))
        .with_default_spacer()
        .with_child(
            Flex::row()
                .with_child(
                    TextBox::new()
                        .with_formatter(ParseFormatter::new())
                        .lens(Params::spacer_size)
                        .fix_width(60.0),
                )
                .with_spacer(druid::theme::WIDGET_CONTROL_COMPONENT_PADDING)
                .with_child(
                    Stepper::new()
                        .with_range(2.0, 50.0)
                        .with_step(2.0)
                        .lens(Params::spacer_size),
                ),
        )
}

fn space_if_needed<T: Data>(flex: &mut Flex<T>, params: &Params) {
    match params.spacers {
        Spacers::None => (),
        Spacers::Default => flex.add_default_spacer(),
        Spacers::Fixed => flex.add_spacer(params.spacer_size),
        Spacers::Flex => flex.add_flex_spacer(1.0),
    }
}

fn build_widget(state: &Params) -> Box<dyn Widget<AppState>> {
    let mut flex = match state.axis {
        FlexType::Column => Flex::column(),
        FlexType::Row => Flex::row(),
    }
    .cross_axis_alignment(state.cross_alignment)
    .main_axis_alignment(state.main_alignment)
    .must_fill_main_axis(state.fill_major_axis);

    flex.add_child(
        TextBox::new()
            .with_placeholder("Sample text")
            .lens(DemoState::input_text),
    );
    space_if_needed(&mut flex, state);

    flex.add_child(
        Button::new("Clear").on_click(|_ctx, data: &mut DemoState, _env| {
            data.input_text.clear();
            data.enabled = false;
            data.volume = 0.0;
        }),
    );

    space_if_needed(&mut flex, state);

    flex.add_child(
        Label::new(|data: &DemoState, _: &Env| data.input_text.clone()).with_text_size(32.0),
    );
    space_if_needed(&mut flex, state);
    flex.add_child(Checkbox::new("Demo").lens(DemoState::enabled));
    space_if_needed(&mut flex, state);
    flex.add_child(Switch::new().lens(DemoState::enabled));
    space_if_needed(&mut flex, state);
    flex.add_child(Slider::new().lens(DemoState::volume));
    space_if_needed(&mut flex, state);
    flex.add_child(ProgressBar::new().lens(DemoState::volume));
    space_if_needed(&mut flex, state);
    flex.add_child(
        Stepper::new()
            .with_range(0.0, 1.0)
            .with_step(0.1)
            .with_wraparound(true)
            .lens(DemoState::volume),
    );

    let mut flex = SizedBox::new(flex);
    if state.fix_minor_axis {
        match state.axis {
            FlexType::Row => flex = flex.height(200.),
            FlexType::Column => flex = flex.width(200.),
        }
    }
    if state.fix_major_axis {
        match state.axis {
            FlexType::Row => flex = flex.width(600.),
            FlexType::Column => flex = flex.height(300.),
        }
    }

    let flex = flex
        .padding(8.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0)
        .lens(AppState::demo_state);

    if state.debug_layout {
        flex.debug_paint_layout().boxed()
    } else {
        flex.boxed()
    }
}
examples/layout.rs (line 70)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
fn build_app() -> impl Widget<u32> {
    // Usually we put all the widgets in one big tree using builder-style
    // methods. Sometimes we split them up in declarations to increase
    // readability. In this case we also have some recurring elements,
    // we add those in a loop later on.
    let mut col = Flex::column().with_child(
        // The `Flex`'s first child is another Flex! In this case it is
        // a row.
        Flex::row()
            // The row has its own children.
            .with_child(
                Label::new("One")
                    .fix_width(60.0)
                    .background(Color::rgb8(0x77, 0x77, 0))
                    .border(Color::WHITE, 3.0)
                    .center(),
            )
            // Spacing element that will fill all available space in
            // between label and a button. Notice that weight is non-zero.
            // We could have achieved a similar result with expanding the
            // width and setting the main-axis-allignment to SpaceBetween.
            .with_flex_spacer(1.0)
            .with_child(Button::new("Two").padding(20.))
            // After we added all the children, we can set some more
            // values using builder-style methods. Since these methods
            // dont return the original `Flex` but a SizedBox and Container
            // respectively, we have to put these at the end.
            .fix_height(100.0)
            //turquoise
            .background(Color::rgb8(0, 0x77, 0x88)),
    );

    for i in 0..5 {
        // Give a larger weight to one of the buttons for it to
        // occupy more space.
        let weight = if i == 2 { 3.0 } else { 1.0 };
        // call `expand_height` to force the buttons to use all their provided flex
        col.add_flex_child(Button::new(format!("Button #{i}")).expand_height(), weight);
    }

    // aspect ratio box
    let aspect_ratio_label = Label::new("This is an aspect-ratio box. Notice how the text will overflow if the box becomes too small.")
        .with_text_color(Color::BLACK)
        .with_line_break_mode(LineBreaking::WordWrap)
        .center();
    let aspect_ratio_box = AspectRatioBox::new(aspect_ratio_label, 4.0)
        .border(Color::BLACK, 1.0)
        .background(Color::WHITE);
    col.add_flex_child(aspect_ratio_box.center(), 1.0);

    // This method asks Druid to draw colored rectangles around our widgets,
    // so we can visually inspect their layout rectangles.
    col.debug_paint_layout()
}
source

fn env_scope(self, f: impl Fn(&mut Env, &T) + 'static) -> EnvScope<T, Self>

Wrap this widget in a EnvScope widget, modifying the parent Env with the provided closure.

Examples found in repository?
examples/styled_text.rs (lines 105-113)
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
157
158
159
160
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);

    // TODO: Replace Parse usage with TextBox::with_formatter
    #[allow(deprecated)]
    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()
}
source

fn controller<C: Controller<T, Self>>( self, controller: C ) -> ControllerHost<Self, C>

Wrap this widget with the provided Controller.

Examples found in repository?
examples/markdown_preview.rs (line 130)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .with_line_break_mode(LineBreaking::WordWrap)
            .lens(AppState::rendered)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE)),
    )
    .vertical()
    .background(Color::grey8(222))
    .expand();

    let textbox = TextBox::multiline()
        .lens(AppState::raw)
        .controller(RichTextRebuilder)
        .expand()
        .padding(5.0);

    Split::columns(label, textbox)
}
More examples
Hide additional examples
examples/event_viewer.rs (lines 236-238)
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
fn interactive_area() -> impl Widget<AppState> {
    let text_box = TextBox::multiline()
        .with_text_color(Color::rgb8(0xf0, 0xf0, 0xea))
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .lens(AppState::text_input)
        .controller(EventLogger {
            filter: |event| matches!(event, Event::KeyDown(_) | Event::KeyUp(_)),
        });

    let mouse_box = SizedBox::empty()
        .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
        .background(CURSOR_BACKGROUND_COLOR)
        .rounded(5.0)
        .border(INTERACTIVE_AREA_BORDER, 1.0)
        .controller(EventLogger {
            filter: |event| {
                matches!(
                    event,
                    Event::MouseDown(_) | Event::MouseUp(_) | Event::Wheel(_)
                )
            },
        });

    Flex::row()
        .with_flex_spacer(1.0)
        .with_child(text_box)
        .with_flex_spacer(1.0)
        .with_child(mouse_box)
        .with_flex_spacer(1.0)
        .padding(10.0)
}
examples/text.rs (line 115)
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
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .controller(LabelController)
            .background(Color::WHITE)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE))
            .background(Color::grey8(222)),
    )
    .vertical();

    let line_break_chooser = Flex::column()
        .with_child(Label::new("Line break mode"))
        .with_spacer(SPACER_SIZE)
        .with_child(RadioGroup::column(vec![
            ("Clip", LineBreaking::Clip),
            ("Wrap", LineBreaking::WordWrap),
            ("Overflow", LineBreaking::Overflow),
        ]))
        .lens(AppState::line_break_mode);

    let alignment_picker = Flex::column()
        .with_child(Label::new("Justification"))
        .with_spacer(SPACER_SIZE)
        .with_child(RadioGroup::column(vec![
            ("Start", TextAlignment::Start),
            ("End", TextAlignment::End),
            ("Center", TextAlignment::Center),
            ("Justified", TextAlignment::Justified),
        ]))
        .lens(AppState::alignment);

    let controls = Flex::row()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(alignment_picker)
        .with_spacer(SPACER_SIZE)
        .with_child(line_break_chooser)
        .padding(SPACER_SIZE);

    Flex::column()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(controls)
        .with_flex_child(label, 1.0)
}
examples/identity.rs (line 74)
62
63
64
65
66
67
68
69
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
fn make_ui() -> impl Widget<OurData> {
    // We can also generate these dynamically whenever we need it.
    let id_two = WidgetId::next();
    // We have a column with 2 labels and 2 buttons.
    // Each of the 2 labels only has access to its own counter and is given a `WidgetId`.
    // Both labels have a controller, this handles commands send to children.
    // The 2 buttons send a command when clicked. Both send the exact same command.
    // The key diference is that they both give a different `WidgetId` as target.
    // This means that only the corresponding controller gets the command, and increments their counter.
    Flex::column()
        .with_child(
            Label::dynamic(|data, _| format!("One: {data}"))
                .controller(LabelControler)
                .with_id(ID_ONE)
                .lens(OurData::counter_one)
                .padding(2.0),
        )
        .with_child(
            Label::dynamic(|data, _| format!("Two: {data}"))
                .controller(LabelControler)
                .with_id(id_two)
                .lens(OurData::counter_two)
                .padding(2.0),
        )
        .with_child(
            Button::<OurData>::new("Increment one")
                .on_click(|ctx, _data, _env| ctx.submit_command(INCREMENT.to(ID_ONE)))
                .padding(2.0),
        )
        .with_child(
            Button::<OurData>::new("Increment two")
                .on_click(move |ctx, _data, _env| ctx.submit_command(INCREMENT.to(id_two)))
                .padding(2.0),
        )
        .padding(10.0)
}
examples/transparency.rs (line 101)
62
63
64
65
66
67
68
69
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
fn build_root_widget() -> impl Widget<HelloState> {
    // Draw red circle, and two semi-transparent rectangles
    let circle_and_rects = Painter::new(|ctx, _data, _env| {
        let boundaries = ctx.size().to_rect();
        let center = (boundaries.width() / 2., boundaries.height() / 2.);
        let circle = Circle::new(center, center.0.min(center.1));
        ctx.fill(circle, &Color::RED);

        let rect1 = Rect::new(0., 0., boundaries.width() / 2., boundaries.height() / 2.);
        ctx.fill(rect1, &Color::rgba8(0x0, 0xff, 0, 125));

        let rect2 = Rect::new(
            boundaries.width() / 2.,
            boundaries.height() / 2.,
            boundaries.width(),
            boundaries.height(),
        );
        ctx.fill(rect2, &Color::rgba8(0x0, 0x0, 0xff, 125));
    });

    // This textbox modifies the label, idea here is to test that the background
    // invalidation works when you type to the textbox
    let textbox = TextBox::new()
        .with_placeholder("Type to test clearing")
        .with_text_size(18.0)
        .lens(HelloState::name)
        .fix_width(250.);

    let label = Label::new(|data: &HelloState, _env: &Env| {
        if data.name.is_empty() {
            "Text: ".to_string()
        } else {
            format!("Text: {}!", data.name)
        }
    })
    .with_text_color(Color::RED)
    .with_text_size(32.0);

    Flex::column()
        .with_flex_child(circle_and_rects.expand().controller(DragController), 10.0)
        .with_spacer(4.0)
        .with_child(textbox)
        .with_spacer(4.0)
        .with_child(label)
}
examples/sub_window.rs (line 351)
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_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)
}
source

fn on_added( self, f: impl Fn(&mut Self, &mut LifeCycleCtx<'_, '_>, &T, &Env) + 'static ) -> ControllerHost<Self, Added<T, Self>>

Provide a closure that will be called when this widget is added to the widget tree.

You can use this to perform any initial setup.

This is equivalent to handling the LifeCycle::WidgetAdded event in a custom Controller.

source

fn on_click( self, f: impl Fn(&mut EventCtx<'_, '_>, &mut T, &Env) + 'static ) -> ControllerHost<Self, Click<T>>

Control the events of this widget with a Click widget. The closure provided will be called when the widget is clicked with the left mouse button.

The child widget will also be updated on LifeCycle::HotChanged and mouse down, which can be useful for painting based on ctx.is_active() and ctx.is_hot().

Examples found in repository?
examples/calc.rs (line 141)
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
Hide additional examples
examples/styled_text.rs (lines 102-104)
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
157
158
159
160
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);

    // TODO: Replace Parse usage with TextBox::with_formatter
    #[allow(deprecated)]
    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()
}
source

fn debug_paint_layout(self) -> EnvScope<T, Self>

Draw the layout Rects of this widget and its children.

Examples found in repository?
examples/svg.rs (line 52)
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
fn ui_builder() -> impl Widget<u32> {
    let tiger_svg = match include_str!("./assets/tiger.svg").parse::<SvgData>() {
        Ok(svg) => svg,
        Err(err) => {
            error!("{}", err);
            error!("Using an empty SVG instead.");
            SvgData::default()
        }
    };

    let mut col = Flex::column();

    col.add_flex_child(Svg::new(tiger_svg.clone()).fix_width(60.0).center(), 1.0);
    col.add_flex_child(Svg::new(tiger_svg.clone()), 1.0);
    col.add_flex_child(Svg::new(tiger_svg), 1.0);
    col.debug_paint_layout()
}
More examples
Hide additional examples
examples/flex.rs (line 308)
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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
fn build_widget(state: &Params) -> Box<dyn Widget<AppState>> {
    let mut flex = match state.axis {
        FlexType::Column => Flex::column(),
        FlexType::Row => Flex::row(),
    }
    .cross_axis_alignment(state.cross_alignment)
    .main_axis_alignment(state.main_alignment)
    .must_fill_main_axis(state.fill_major_axis);

    flex.add_child(
        TextBox::new()
            .with_placeholder("Sample text")
            .lens(DemoState::input_text),
    );
    space_if_needed(&mut flex, state);

    flex.add_child(
        Button::new("Clear").on_click(|_ctx, data: &mut DemoState, _env| {
            data.input_text.clear();
            data.enabled = false;
            data.volume = 0.0;
        }),
    );

    space_if_needed(&mut flex, state);

    flex.add_child(
        Label::new(|data: &DemoState, _: &Env| data.input_text.clone()).with_text_size(32.0),
    );
    space_if_needed(&mut flex, state);
    flex.add_child(Checkbox::new("Demo").lens(DemoState::enabled));
    space_if_needed(&mut flex, state);
    flex.add_child(Switch::new().lens(DemoState::enabled));
    space_if_needed(&mut flex, state);
    flex.add_child(Slider::new().lens(DemoState::volume));
    space_if_needed(&mut flex, state);
    flex.add_child(ProgressBar::new().lens(DemoState::volume));
    space_if_needed(&mut flex, state);
    flex.add_child(
        Stepper::new()
            .with_range(0.0, 1.0)
            .with_step(0.1)
            .with_wraparound(true)
            .lens(DemoState::volume),
    );

    let mut flex = SizedBox::new(flex);
    if state.fix_minor_axis {
        match state.axis {
            FlexType::Row => flex = flex.height(200.),
            FlexType::Column => flex = flex.width(200.),
        }
    }
    if state.fix_major_axis {
        match state.axis {
            FlexType::Row => flex = flex.width(600.),
            FlexType::Column => flex = flex.height(300.),
        }
    }

    let flex = flex
        .padding(8.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0)
        .lens(AppState::demo_state);

    if state.debug_layout {
        flex.debug_paint_layout().boxed()
    } else {
        flex.boxed()
    }
}
examples/layout.rs (line 76)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
fn build_app() -> impl Widget<u32> {
    // Usually we put all the widgets in one big tree using builder-style
    // methods. Sometimes we split them up in declarations to increase
    // readability. In this case we also have some recurring elements,
    // we add those in a loop later on.
    let mut col = Flex::column().with_child(
        // The `Flex`'s first child is another Flex! In this case it is
        // a row.
        Flex::row()
            // The row has its own children.
            .with_child(
                Label::new("One")
                    .fix_width(60.0)
                    .background(Color::rgb8(0x77, 0x77, 0))
                    .border(Color::WHITE, 3.0)
                    .center(),
            )
            // Spacing element that will fill all available space in
            // between label and a button. Notice that weight is non-zero.
            // We could have achieved a similar result with expanding the
            // width and setting the main-axis-allignment to SpaceBetween.
            .with_flex_spacer(1.0)
            .with_child(Button::new("Two").padding(20.))
            // After we added all the children, we can set some more
            // values using builder-style methods. Since these methods
            // dont return the original `Flex` but a SizedBox and Container
            // respectively, we have to put these at the end.
            .fix_height(100.0)
            //turquoise
            .background(Color::rgb8(0, 0x77, 0x88)),
    );

    for i in 0..5 {
        // Give a larger weight to one of the buttons for it to
        // occupy more space.
        let weight = if i == 2 { 3.0 } else { 1.0 };
        // call `expand_height` to force the buttons to use all their provided flex
        col.add_flex_child(Button::new(format!("Button #{i}")).expand_height(), weight);
    }

    // aspect ratio box
    let aspect_ratio_label = Label::new("This is an aspect-ratio box. Notice how the text will overflow if the box becomes too small.")
        .with_text_color(Color::BLACK)
        .with_line_break_mode(LineBreaking::WordWrap)
        .center();
    let aspect_ratio_box = AspectRatioBox::new(aspect_ratio_label, 4.0)
        .border(Color::BLACK, 1.0)
        .background(Color::WHITE);
    col.add_flex_child(aspect_ratio_box.center(), 1.0);

    // This method asks Druid to draw colored rectangles around our widgets,
    // so we can visually inspect their layout rectangles.
    col.debug_paint_layout()
}
examples/list.rs (line 146)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
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
                data.l_index += 1;
                data.left.push_back(data.l_index as u32);

                // Add child to right list
                data.r_index += 1;
                data.right.push_back(data.r_index as u32);
            })
            .fix_height(30.0)
            .expand_width(),
    );

    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 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}")
                        })
                        .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)
            })
            .with_spacing(10.),
        )
        .vertical()
        .lens(lens::Identity.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.with_child(Label::new("horizontal list"))
        .with_child(
            Scroll::new(
                List::new(|| {
                    Label::new(|item: &u32, _env: &_| format!("List item #{item}"))
                        .padding(10.0)
                        .background(Color::rgb(0.5, 0.5, 0.0))
                        .fix_height(50.0)
                })
                .horizontal()
                .with_spacing(10.)
                .lens(AppData::left),
            )
            .horizontal(),
        )
        .debug_paint_layout()
}
source

fn debug_widget_id(self) -> EnvScope<T, Self>

Display the WidgetIds for this widget and its children, when hot.

When this is true, widgets that are hot (are under the mouse cursor) will display their ids in their bottom right corner.

These ids may overlap; in this case the id of a child will obscure the id of its parent.

source

fn debug_invalidation(self) -> DebugInvalidation<T, Self>

Draw a color-changing rectangle over this widget, allowing you to see the invalidation regions.

Examples found in repository?
examples/invalidation.rs (line 55)
49
50
51
52
53
54
55
56
fn build_widget() -> impl Widget<AppState> {
    let mut col = Flex::column();
    col.add_child(TextBox::new().lens(AppState::label).padding(3.0));
    for i in 0..30 {
        col.add_child(Button::new(format!("Button {i}")).padding(3.0));
    }
    Split::columns(Scroll::new(col), CircleView.lens(AppState::circles)).debug_invalidation()
}
source

fn debug_widget(self) -> EnvScope<T, Self>

Set the DEBUG_WIDGET env variable for this widget (and its descendants).

This does nothing by default, but you can use this variable while debugging to only print messages from particular instances of a widget.

source

fn lens<S: Data, L: Lens<S, T>>(self, lens: L) -> LensWrap<S, T, L, Self>

Wrap this widget in a LensWrap widget for the provided Lens.

Examples found in repository?
examples/invalidation.rs (line 51)
49
50
51
52
53
54
55
56
fn build_widget() -> impl Widget<AppState> {
    let mut col = Flex::column();
    col.add_child(TextBox::new().lens(AppState::label).padding(3.0));
    for i in 0..30 {
        col.add_child(Button::new(format!("Button {i}")).padding(3.0));
    }
    Split::columns(Scroll::new(col), CircleView.lens(AppState::circles)).debug_invalidation()
}
More examples
Hide additional examples
examples/markdown_preview.rs (line 120)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
fn build_root_widget() -> impl Widget<AppState> {
    let label = Scroll::new(
        RawLabel::new()
            .with_text_color(Color::BLACK)
            .with_line_break_mode(LineBreaking::WordWrap)
            .lens(AppState::rendered)
            .expand_width()
            .padding((SPACER_SIZE * 4.0, SPACER_SIZE)),
    )
    .vertical()
    .background(Color::grey8(222))
    .expand();

    let textbox = TextBox::multiline()
        .lens(AppState::raw)
        .controller(RichTextRebuilder)
        .expand()
        .padding(5.0);

    Split::columns(label, textbox)
}
examples/either.rs (line 35)
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
fn ui_builder() -> impl Widget<AppState> {
    // Our UI consists of a column with a button and an `Either` widget
    let button = Checkbox::new("Toggle slider")
        .lens(AppState::which)
        .padding(5.0);

    // The `Either` widget has two children, only one of which is visible at a time.
    // To determine which child is visible, you pass it a closure that takes the
    // `Data` and the `Env` and returns a bool; if it returns `true`, the first
    // widget will be visible, and if `false`, the second.
    let either = Either::new(
        |data, _env| data.which,
        Slider::new().lens(AppState::value).padding(5.0),
        Label::new("Click to reveal slider").padding(5.0),
    );
    Flex::column().with_child(button).with_child(either)
}
examples/textbox.rs (line 81)
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
fn build_root_widget() -> impl Widget<AppState> {
    let blurb = Label::new(EXPLAINER)
        .with_line_break_mode(druid::widget::LineBreaking::WordWrap)
        .padding(8.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0);

    Flex::column()
        .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
        .with_child(blurb)
        .with_spacer(24.0)
        .with_child(
            TextBox::new()
                .with_placeholder("Single")
                .lens(AppState::single),
        )
        .with_default_spacer()
        .with_flex_child(
            TextBox::multiline()
                .with_placeholder("Multi")
                .lens(AppState::multi)
                .expand_width(),
            1.0,
        )
        .padding(8.0)
}
examples/hello.rs (line 67)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
fn build_root_widget() -> impl Widget<HelloState> {
    // a label that will determine its text based on the current app data.
    let label = Label::new(|data: &HelloState, _env: &Env| {
        if data.name.is_empty() {
            "Hello anybody!?".to_string()
        } else {
            format!("Hello {}!", data.name)
        }
    })
    .with_text_size(32.0);

    // a textbox that modifies `name`.
    let textbox = TextBox::new()
        .with_placeholder("Who are we greeting?")
        .with_text_size(18.0)
        .fix_width(TEXT_BOX_WIDTH)
        .lens(HelloState::name);

    // arrange the two widgets vertically, with some padding
    Flex::column()
        .with_child(label)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(textbox)
        .align_vertical(UnitPoint::CENTER)
}
examples/lens.rs (line 47)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
fn ui_builder() -> impl Widget<MyComplexState> {
    // `TextBox` is of type `Widget<String>`
    // via `.lens` we get it to be of type `Widget<MyComplexState>`
    let searchbar = TextBox::new().lens(MyComplexState::term_lens);

    // `Slider` is of type `Widget<f64>`
    // via `.lens` we get it to be of type `Widget<MyComplexState>`
    let slider = Slider::new().lens(MyComplexState::scale);

    let label = Label::new(|d: &MyComplexState, _: &Env| format!("{}: {:.2}", d.term, d.scale));

    Flex::column()
        .cross_axis_alignment(CrossAxisAlignment::Center)
        .with_child(label)
        .with_default_spacer()
        .with_child(
            Flex::row()
                .cross_axis_alignment(CrossAxisAlignment::Center)
                .with_child(searchbar)
                .with_default_spacer()
                .with_child(slider),
        )
        .center()
}
source

fn with_id(self, id: WidgetId) -> IdentityWrapper<Self>

Assign the widget a specific WidgetId.

You must ensure that a given WidgetId is only ever used for a single widget at a time.

An id may be reused over time; for instance if you replace one widget with another, you may reuse the first widget’s id.

Examples found in repository?
examples/identity.rs (line 75)
62
63
64
65
66
67
68
69
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
fn make_ui() -> impl Widget<OurData> {
    // We can also generate these dynamically whenever we need it.
    let id_two = WidgetId::next();
    // We have a column with 2 labels and 2 buttons.
    // Each of the 2 labels only has access to its own counter and is given a `WidgetId`.
    // Both labels have a controller, this handles commands send to children.
    // The 2 buttons send a command when clicked. Both send the exact same command.
    // The key diference is that they both give a different `WidgetId` as target.
    // This means that only the corresponding controller gets the command, and increments their counter.
    Flex::column()
        .with_child(
            Label::dynamic(|data, _| format!("One: {data}"))
                .controller(LabelControler)
                .with_id(ID_ONE)
                .lens(OurData::counter_one)
                .padding(2.0),
        )
        .with_child(
            Label::dynamic(|data, _| format!("Two: {data}"))
                .controller(LabelControler)
                .with_id(id_two)
                .lens(OurData::counter_two)
                .padding(2.0),
        )
        .with_child(
            Button::<OurData>::new("Increment one")
                .on_click(|ctx, _data, _env| ctx.submit_command(INCREMENT.to(ID_ONE)))
                .padding(2.0),
        )
        .with_child(
            Button::<OurData>::new("Increment two")
                .on_click(move |ctx, _data, _env| ctx.submit_command(INCREMENT.to(id_two)))
                .padding(2.0),
        )
        .padding(10.0)
}
source

fn boxed(self) -> Box<dyn Widget<T>>

Wrap this widget in a Box.

Examples found in repository?
examples/flex.rs (line 104)
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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
    fn new() -> Rebuilder {
        Rebuilder {
            inner: SizedBox::empty().boxed(),
        }
    }

    fn rebuild_inner(&mut self, data: &AppState) {
        self.inner = build_widget(&data.params);
    }
}

impl Widget<AppState> for Rebuilder {
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut AppState, env: &Env) {
        self.inner.event(ctx, event, data, env)
    }

    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &AppState, env: &Env) {
        if let LifeCycle::WidgetAdded = event {
            self.rebuild_inner(data);
        }
        self.inner.lifecycle(ctx, event, data, env)
    }

    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &AppState, data: &AppState, env: &Env) {
        if !old_data.params.same(&data.params) {
            self.rebuild_inner(data);
            ctx.children_changed();
        } else {
            self.inner.update(ctx, old_data, data, env);
        }
    }

    fn layout(
        &mut self,
        ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        data: &AppState,
        env: &Env,
    ) -> Size {
        self.inner.layout(ctx, bc, data, env)
    }

    fn paint(&mut self, ctx: &mut PaintCtx, data: &AppState, env: &Env) {
        self.inner.paint(ctx, data, env)
    }

    fn id(&self) -> Option<WidgetId> {
        self.inner.id()
    }
}

fn make_control_row() -> impl Widget<AppState> {
    Flex::row()
        .cross_axis_alignment(CrossAxisAlignment::Start)
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("Type:"))
                .with_default_spacer()
                .with_child(RadioGroup::column(FLEX_TYPE_OPTIONS.to_vec()).lens(Params::axis)),
        )
        .with_default_spacer()
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("CrossAxis:"))
                .with_default_spacer()
                .with_child(
                    RadioGroup::column(CROSS_AXIS_ALIGNMENT_OPTIONS.to_vec())
                        .lens(Params::cross_alignment),
                ),
        )
        .with_default_spacer()
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("MainAxis:"))
                .with_default_spacer()
                .with_child(
                    RadioGroup::column(MAIN_AXIS_ALIGNMENT_OPTIONS.to_vec())
                        .lens(Params::main_alignment),
                ),
        )
        .with_default_spacer()
        .with_child(make_spacer_select())
        .with_default_spacer()
        .with_child(
            Flex::column()
                .cross_axis_alignment(CrossAxisAlignment::Start)
                .with_child(Label::new("Misc:"))
                .with_default_spacer()
                .with_child(Checkbox::new("Debug layout").lens(Params::debug_layout))
                .with_default_spacer()
                .with_child(Checkbox::new("Fill main axis").lens(Params::fill_major_axis))
                .with_default_spacer()
                .with_child(Checkbox::new("Fix minor axis size").lens(Params::fix_minor_axis))
                .with_default_spacer()
                .with_child(Checkbox::new("Fix major axis size").lens(Params::fix_major_axis)),
        )
        .padding(10.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0)
        .lens(AppState::params)
}

fn make_spacer_select() -> impl Widget<Params> {
    Flex::column()
        .cross_axis_alignment(CrossAxisAlignment::Start)
        .with_child(Label::new("Insert Spacers:"))
        .with_default_spacer()
        .with_child(RadioGroup::column(SPACER_OPTIONS.to_vec()).lens(Params::spacers))
        .with_default_spacer()
        .with_child(
            Flex::row()
                .with_child(
                    TextBox::new()
                        .with_formatter(ParseFormatter::new())
                        .lens(Params::spacer_size)
                        .fix_width(60.0),
                )
                .with_spacer(druid::theme::WIDGET_CONTROL_COMPONENT_PADDING)
                .with_child(
                    Stepper::new()
                        .with_range(2.0, 50.0)
                        .with_step(2.0)
                        .lens(Params::spacer_size),
                ),
        )
}

fn space_if_needed<T: Data>(flex: &mut Flex<T>, params: &Params) {
    match params.spacers {
        Spacers::None => (),
        Spacers::Default => flex.add_default_spacer(),
        Spacers::Fixed => flex.add_spacer(params.spacer_size),
        Spacers::Flex => flex.add_flex_spacer(1.0),
    }
}

fn build_widget(state: &Params) -> Box<dyn Widget<AppState>> {
    let mut flex = match state.axis {
        FlexType::Column => Flex::column(),
        FlexType::Row => Flex::row(),
    }
    .cross_axis_alignment(state.cross_alignment)
    .main_axis_alignment(state.main_alignment)
    .must_fill_main_axis(state.fill_major_axis);

    flex.add_child(
        TextBox::new()
            .with_placeholder("Sample text")
            .lens(DemoState::input_text),
    );
    space_if_needed(&mut flex, state);

    flex.add_child(
        Button::new("Clear").on_click(|_ctx, data: &mut DemoState, _env| {
            data.input_text.clear();
            data.enabled = false;
            data.volume = 0.0;
        }),
    );

    space_if_needed(&mut flex, state);

    flex.add_child(
        Label::new(|data: &DemoState, _: &Env| data.input_text.clone()).with_text_size(32.0),
    );
    space_if_needed(&mut flex, state);
    flex.add_child(Checkbox::new("Demo").lens(DemoState::enabled));
    space_if_needed(&mut flex, state);
    flex.add_child(Switch::new().lens(DemoState::enabled));
    space_if_needed(&mut flex, state);
    flex.add_child(Slider::new().lens(DemoState::volume));
    space_if_needed(&mut flex, state);
    flex.add_child(ProgressBar::new().lens(DemoState::volume));
    space_if_needed(&mut flex, state);
    flex.add_child(
        Stepper::new()
            .with_range(0.0, 1.0)
            .with_step(0.1)
            .with_wraparound(true)
            .lens(DemoState::volume),
    );

    let mut flex = SizedBox::new(flex);
    if state.fix_minor_axis {
        match state.axis {
            FlexType::Row => flex = flex.height(200.),
            FlexType::Column => flex = flex.width(200.),
        }
    }
    if state.fix_major_axis {
        match state.axis {
            FlexType::Row => flex = flex.width(600.),
            FlexType::Column => flex = flex.height(300.),
        }
    }

    let flex = flex
        .padding(8.0)
        .border(Color::grey(0.6), 2.0)
        .rounded(5.0)
        .lens(AppState::demo_state);

    if state.debug_layout {
        flex.debug_paint_layout().boxed()
    } else {
        flex.boxed()
    }
}
More examples
Hide additional examples
examples/slider.rs (line 87)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
fn build_root_widget() -> impl Widget<AppState> {
    let range = Flex::row()
        .with_child(Label::dynamic(|value: &(f64, f64), _| {
            format!("Value Range: {value:?}")
        }))
        .with_default_spacer()
        .with_child(
            RangeSlider::new()
                .with_range(0.0, 20.0)
                .with_step(1.0)
                .track_color(KeyOrValue::Concrete(Color::RED))
                .fix_width(250.0),
        )
        .lens(AppState::range);

    let value = Flex::row()
        .with_child(Label::dynamic(|value: &AppState, _| {
            format!("Value: {:?}", value.value)
        }))
        .with_default_spacer()
        .with_child(ViewSwitcher::new(
            |data: &AppState, _| data.range,
            |range, _, _| {
                Slider::new()
                    .with_range(range.0, range.1)
                    .track_color(KeyOrValue::Concrete(Color::RED))
                    .knob_style(KnobStyle::Wedge)
                    .axis(Axis::Vertical)
                    .with_step(0.25)
                    .annotated(1.0, 0.25)
                    .fix_height(250.0)
                    .lens(AppState::value)
                    .boxed()
            },
        ));

    // arrange the two widgets vertically, with some padding
    Flex::column()
        .with_child(range)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(value)
        .cross_axis_alignment(CrossAxisAlignment::End)
        .align_vertical(UnitPoint::RIGHT)
        .padding(20.0)
}
source

fn scroll(self) -> Scroll<T, Self>

Wrap this widget in a Scroll widget.

Examples found in repository?
examples/scroll.rs (line 41)
36
37
38
39
40
41
42
fn build_widget() -> impl Widget<u32> {
    let mut col = Flex::column();
    for i in 0..30 {
        col.add_child(Padding::new(3.0, OverPainter(i)));
    }
    col.scroll()
}
source

fn disabled_if( self, disabled_if: impl Fn(&T, &Env) -> bool + 'static ) -> DisabledIf<T, Self>

Wrap this widget in a DisabledIf widget.

The provided closure will determine if the widget is disabled. See is_disabled or set_disabled for more info about disabled state.

Examples found in repository?
examples/disabled.rs (line 50)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
fn main_widget() -> impl Widget<AppData> {
    Flex::column()
        .with_child(named_child("text:", TextBox::new().lens(AppData::text)))
        .with_default_spacer()
        .with_child(
            named_child("text (disabled):", TextBox::new().lens(AppData::text))
                .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(named_child("text:", TextBox::new().lens(AppData::text)))
        .with_default_spacer()
        .with_child(
            named_child("text (disabled):", TextBox::new().lens(AppData::text))
                .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_default_spacer()
        .with_child(
            named_child(
                "value (disabled):",
                Slider::new().with_range(0.0, 10.0).lens(AppData::value),
            )
            .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            named_child(
                "value (disabled):",
                Stepper::new()
                    .with_range(0.0, 10.0)
                    .with_step(0.5)
                    .lens(AppData::value),
            )
            .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            named_child(
                "option (disabled):",
                Checkbox::new("option").lens(AppData::option),
            )
            .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            named_child("option (disabled):", Switch::new().lens(AppData::option))
                .disabled_if(|data, _| data.disabled),
        )
        .with_default_spacer()
        .with_child(
            Flex::row()
                .with_child(
                    Button::new("-")
                        .on_click(|_, data: &mut f64, _| *data -= 1.0)
                        .disabled_if(|data, _| *data < 1.0),
                )
                .with_default_spacer()
                .with_child(Label::dynamic(|data: &f64, _| data.to_string()))
                .with_default_spacer()
                .with_child(
                    Button::new("+")
                        .on_click(|_, data: &mut f64, _| *data += 1.0)
                        .disabled_if(|data, _| *data > 9.0),
                )
                .lens(AppData::value)
                .disabled_if(|data: &AppData, _| data.disabled),
        )
        .with_default_spacer()
        .with_default_spacer()
        .with_default_spacer()
        .with_child(Checkbox::new("disabled").lens(AppData::disabled))
        .with_default_spacer()
        .cross_axis_alignment(CrossAxisAlignment::End)
        .align_horizontal(UnitPoint::CENTER)
}

Implementors§

source§

impl<T: Data, W: Widget<T> + 'static> WidgetExt<T> for W