Struct druid::widget::prelude::PaintCtx

source ·
pub struct PaintCtx<'a, 'b, 'c> {
    pub render_ctx: &'a mut Piet<'c>,
    /* private fields */
}
Expand description

A context passed to paint methods of widgets.

In addition to the API below, PaintCtx derefs to an implementation of the RenderContext trait, which defines the basic available drawing commands.

Fields§

§render_ctx: &'a mut Piet<'c>

The render context for actually painting.

Implementations§

source§

impl PaintCtx<'_, '_, '_>

source

pub fn widget_id(&self) -> WidgetId

get the WidgetId of the current widget.

source

pub fn window(&self) -> &WindowHandle

Returns a reference to the current WindowHandle.

source

pub fn window_id(&self) -> WindowId

Get the WindowId of the current window.

source

pub fn text(&mut self) -> &mut PietText

Get an object which can create text layouts.

Examples found in repository?
examples/sub_window.rs (line 293)
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
    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), env: &Env) {
        let sz = ctx.size();

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

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

                if let Ok(tl) = ctx
                    .text()
                    .new_text_layout(format!(
                        "{}:{}x{}@{},{}",
                        i,
                        vr.width(),
                        vr.height(),
                        vr.x0,
                        vr.y0
                    ))
                    .max_width(tr.width() - 5.)
                    .font(font.clone(), env.get(theme::TEXT_SIZE_NORMAL))
                    .text_color(Color::WHITE)
                    .build()
                {
                    ctx.draw_text(&tl, tr.center() - tl.size().to_vec2() * 0.5);
                }
            }
        }
    }
More examples
Hide additional examples
examples/custom_widget.rs (line 121)
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
    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
        // Clear the whole widget with the color of your choice
        // (ctx.size() returns the size of the layout rect we're painting in)
        // Note: ctx also has a `clear` method, but that clears the whole context,
        // and we only want to clear this widget's area.
        let size = ctx.size();
        let rect = size.to_rect();
        ctx.fill(rect, &Color::WHITE);

        // We can paint with a Z index, this indicates that this code will be run
        // after the rest of the painting. Painting with z-index is done in order,
        // so first everything with z-index 1 is painted and then with z-index 2 etc.
        // As you can see this(red) curve is drawn on top of the green curve
        ctx.paint_with_z_index(1, move |ctx| {
            let mut path = BezPath::new();
            path.move_to((0.0, size.height));
            path.quad_to((40.0, 50.0), (size.width, 0.0));
            // Create a color
            let stroke_color = Color::rgb8(128, 0, 0);
            // Stroke the path with thickness 1.0
            ctx.stroke(path, &stroke_color, 5.0);
        });

        // Create an arbitrary bezier path
        let mut path = BezPath::new();
        path.move_to(Point::ORIGIN);
        path.quad_to((40.0, 50.0), (size.width, size.height));
        // Create a color
        let stroke_color = Color::rgb8(0, 128, 0);
        // Stroke the path with thickness 5.0
        ctx.stroke(path, &stroke_color, 5.0);

        // Rectangles: the path for practical people
        let rect = Rect::from_origin_size((10.0, 10.0), (100.0, 100.0));
        // Note the Color:rgba8 which includes an alpha channel (7F in this case)
        let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
        ctx.fill(rect, &fill_color);

        // Text is easy; in real use TextLayout should either be stored in the
        // widget and reused, or a label child widget to manage it all.
        // This is one way of doing it, you can also use a builder-style way.
        let mut layout = TextLayout::<String>::from_text(data);
        layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
        layout.set_text_color(fill_color);
        layout.rebuild_if_needed(ctx.text(), env);

        // Let's rotate our text slightly. First we save our current (default) context:
        ctx.with_save(|ctx| {
            // Now we can rotate the context (or set a clip path, for instance):
            // This makes it so that anything drawn after this (in the closure) is
            // transformed.
            // The transformation is in radians, but be aware it transforms the canvas,
            // not just the part you are drawing. So we draw at (80.0, 40.0) on the rotated
            // canvas, this is NOT the same position as (80.0, 40.0) on the original canvas.
            ctx.transform(Affine::rotate(std::f64::consts::FRAC_PI_4));
            layout.draw(ctx, (80.0, 40.0));
        });
        // When we exit with_save, the original context's rotation is restored

        // This is the builder-style way of drawing text.
        let text = ctx.text();
        let layout = text
            .new_text_layout(data.clone())
            .font(FontFamily::SERIF, 24.0)
            .text_color(Color::rgb8(128, 0, 0))
            .build()
            .unwrap();
        ctx.draw_text(&layout, (100.0, 25.0));

        // Let's burn some CPU to make a (partially transparent) image buffer
        let image_data = make_image_data(256, 256);
        let image = ctx
            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
            .unwrap();
        // The image is automatically scaled to fit the rect you pass to draw_image
        ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear);
    }
source

pub fn scale(&self) -> Scale

The current window’s Scale.

The returned Scale is a copy and thus its information will be stale after the platform changes the window’s scale. This means you can only rely on it until the next Event::WindowScale event happens.

source§

impl PaintCtx<'_, '_, '_>

source

pub fn size(&self) -> Size

The layout size.

This is the layout size as ultimately determined by the parent container, on the previous layout pass.

Generally it will be the same as the size returned by the child widget’s layout method.

Examples found in repository?
examples/async_event.rs (line 83)
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/scroll.rs (line 62)
61
62
63
64
65
66
67
68
69
    fn paint(&mut self, ctx: &mut PaintCtx, _: &T, env: &Env) {
        let rect = Rect::ZERO.with_size(ctx.size());
        let color = env.get_debug_color(self.0);
        let radius = (rect + INSETS).size().height / 2.0;
        let circle = Circle::new(rect.center(), radius);
        let grad = RadialGradient::new(1.0, (color, color.with_alpha(0.0)));
        ctx.fill(circle, &grad);
        ctx.stroke(rect, &color, 2.0);
    }
examples/invalidation.rs (line 139)
137
138
139
140
141
142
143
144
145
146
147
148
149
    fn paint(&mut self, ctx: &mut PaintCtx, data: &Vector<Circle>, _env: &Env) {
        ctx.with_save(|ctx| {
            let rect = ctx.size().to_rect();
            ctx.clip(rect);
            ctx.fill(rect, &Color::WHITE);
            let now = Instant::now();
            for c in data {
                let color =
                    Color::BLACK.with_alpha(now.duration_since(c.time).as_secs_f64().cos().abs());
                ctx.fill(kurbo::Circle::new(c.pos, RADIUS), &color);
            }
        });
    }
examples/calc.rs (line 123)
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/input_region.rs (line 171)
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
    fn paint(&mut self, ctx: &mut druid::PaintCtx, data: &AppState, env: &druid::Env) {
        let window_area = ctx.size();
        let left_rect = Rect::new(0.0, 0.0, EXAMPLE_BORDER_SIZE, window_area.height);
        let right_rect = Rect::new(
            window_area.width - EXAMPLE_BORDER_SIZE,
            0.0,
            window_area.width,
            window_area.height,
        );
        let bottom_rect = Rect::new(
            0.0,
            window_area.height - EXAMPLE_BORDER_SIZE,
            window_area.width,
            window_area.height,
        );

        ctx.fill(left_rect, &Color::rgba(1.0, 0., 0., 0.7));
        ctx.fill(right_rect, &Color::rgba(1.0, 0., 0., 0.7));
        ctx.fill(bottom_rect, &Color::rgba(1.0, 0., 0., 0.7));
        self.info_label.paint(ctx, data, env);
        self.controls.paint(ctx, data, env);
    }
examples/game_of_life.rs (line 298)
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
    fn paint(&mut self, ctx: &mut PaintCtx, data: &AppData, _env: &Env) {
        let size: Size = ctx.size();
        let w0 = size.width / GRID_SIZE as f64;
        let h0 = size.height / GRID_SIZE as f64;
        let cell_size = Size {
            width: w0,
            height: h0,
        };
        self.cell_size = cell_size;
        for row in 0..GRID_SIZE {
            for col in 0..GRID_SIZE {
                let pos = GridPos { row, col };
                if data.grid[pos] {
                    let point = Point {
                        x: w0 * row as f64,
                        y: h0 * col as f64,
                    };
                    let rect = Rect::from_origin_size(point, cell_size);

                    // We divide by 2 so that the colour changes every 2 positions instead of every 1
                    ctx.fill(
                        rect,
                        &COLOURS[((pos.row * GRID_SIZE + pos.col) / 2) % COLOURS.len()],
                    );
                }
            }
        }
    }
source

pub fn window_origin(&self) -> Point

The origin of the widget in window coordinates, relative to the top left corner of the content area.

source

pub fn to_window(&self, widget_point: Point) -> Point

Convert a point from the widget’s coordinate space to the window’s.

The returned point is relative to the content area; it excludes window chrome.

source

pub fn to_screen(&self, widget_point: Point) -> Point

Convert a point from the widget’s coordinate space to the screen’s. See the Screen module

source

pub fn is_hot(&self) -> bool

Query the “hot” state of the widget.

See WidgetPod::is_hot for additional information.

Examples found in repository?
examples/multiwin.rs (line 120)
119
120
121
122
123
124
    fn paint(&mut self, ctx: &mut PaintCtx, data: &State, env: &Env) {
        if data.glow_hot && ctx.is_hot() {
            BackgroundBrush::Color(Color::rgb8(200, 55, 55)).paint(ctx, data, env);
        }
        self.inner.paint(ctx, data, env);
    }
More examples
Hide additional examples
examples/timer.rs (line 112)
111
112
113
114
115
116
117
118
    fn paint(&mut self, ctx: &mut PaintCtx, data: &u32, env: &Env) {
        let mut background = if ctx.is_hot() {
            BackgroundBrush::Color(Color::rgb8(200, 55, 55))
        } else {
            BackgroundBrush::Color(Color::rgb8(30, 210, 170))
        };
        background.paint(ctx, data, env);
    }
examples/calc.rs (line 127)
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/styled_text.rs (line 75)
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

pub fn is_active(&self) -> bool

Query the “active” state of the widget.

See WidgetPod::is_active for additional information.

Examples found in repository?
examples/calc.rs (line 131)
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 (line 79)
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

pub fn is_focused(&self) -> bool

The focus status of a widget.

Returns true if this specific widget is focused. To check if any descendants are focused use has_focus.

Focus means that the widget receives keyboard events.

A widget can request focus using the request_focus method. It’s also possible to register for automatic focus via register_for_focus.

If a widget gains or loses focus it will get a LifeCycle::FocusChanged event.

Only one widget at a time is focused. However due to the way events are routed, all ancestors of that widget will also receive keyboard events.

source

pub fn has_focus(&self) -> bool

The (tree) focus status of a widget.

Returns true if either this specific widget or any one of its descendants is focused. To check if only this specific widget is focused use is_focused,

source

pub fn is_disabled(&self) -> bool

The disabled state of a widget.

Returns true if this widget or any of its ancestors is explicitly disabled. To make this widget explicitly disabled use set_disabled.

Disabled means that this widget should not change the state of the application. What that means is not entirely clear but in any it should not change its data. Therefore others can use this as a safety mechanism to prevent the application from entering an illegal state. For an example the decrease button of a counter of type usize should be disabled if the value is 0.

source§

impl PaintCtx<'_, '_, '_>

source

pub fn depth(&self) -> u32

The depth in the tree of the currently painting widget.

This may be used in combination with paint_with_z_index in order to correctly order painting operations.

The depth here may not be exact; it is only guaranteed that a child will have a greater depth than its parent.

source

pub fn region(&self) -> &Region

Returns the region that needs to be repainted.

source

pub fn with_child_ctx( &mut self, region: impl Into<Region>, f: impl FnOnce(&mut PaintCtx<'_, '_, '_>) )

Creates a temporary PaintCtx with a new visible region, and calls the provided function with that PaintCtx.

This is used by containers to ensure that their children have the correct visible region given their layout.

source

pub fn with_save(&mut self, f: impl FnOnce(&mut PaintCtx<'_, '_, '_>))

Saves the current context, executes the closures, and restores the context.

This is useful if you would like to transform or clip or otherwise modify the drawing context but do not want that modification to effect other widgets.

Examples
fn paint(&mut self, ctx: &mut PaintCtx, _data: &T, env: &Env) {
    let clip_rect = ctx.size().to_rect().inset(5.0);
    ctx.with_save(|ctx| {
        ctx.clip(clip_rect);
        ctx.stroke(clip_rect, &env.get(theme::PRIMARY_DARK), 5.0);
    });
}
Examples found in repository?
examples/invalidation.rs (lines 138-148)
137
138
139
140
141
142
143
144
145
146
147
148
149
    fn paint(&mut self, ctx: &mut PaintCtx, data: &Vector<Circle>, _env: &Env) {
        ctx.with_save(|ctx| {
            let rect = ctx.size().to_rect();
            ctx.clip(rect);
            ctx.fill(rect, &Color::WHITE);
            let now = Instant::now();
            for c in data {
                let color =
                    Color::BLACK.with_alpha(now.duration_since(c.time).as_secs_f64().cos().abs());
                ctx.fill(kurbo::Circle::new(c.pos, RADIUS), &color);
            }
        });
    }
More examples
Hide additional examples
examples/custom_widget.rs (lines 124-133)
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
    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
        // Clear the whole widget with the color of your choice
        // (ctx.size() returns the size of the layout rect we're painting in)
        // Note: ctx also has a `clear` method, but that clears the whole context,
        // and we only want to clear this widget's area.
        let size = ctx.size();
        let rect = size.to_rect();
        ctx.fill(rect, &Color::WHITE);

        // We can paint with a Z index, this indicates that this code will be run
        // after the rest of the painting. Painting with z-index is done in order,
        // so first everything with z-index 1 is painted and then with z-index 2 etc.
        // As you can see this(red) curve is drawn on top of the green curve
        ctx.paint_with_z_index(1, move |ctx| {
            let mut path = BezPath::new();
            path.move_to((0.0, size.height));
            path.quad_to((40.0, 50.0), (size.width, 0.0));
            // Create a color
            let stroke_color = Color::rgb8(128, 0, 0);
            // Stroke the path with thickness 1.0
            ctx.stroke(path, &stroke_color, 5.0);
        });

        // Create an arbitrary bezier path
        let mut path = BezPath::new();
        path.move_to(Point::ORIGIN);
        path.quad_to((40.0, 50.0), (size.width, size.height));
        // Create a color
        let stroke_color = Color::rgb8(0, 128, 0);
        // Stroke the path with thickness 5.0
        ctx.stroke(path, &stroke_color, 5.0);

        // Rectangles: the path for practical people
        let rect = Rect::from_origin_size((10.0, 10.0), (100.0, 100.0));
        // Note the Color:rgba8 which includes an alpha channel (7F in this case)
        let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
        ctx.fill(rect, &fill_color);

        // Text is easy; in real use TextLayout should either be stored in the
        // widget and reused, or a label child widget to manage it all.
        // This is one way of doing it, you can also use a builder-style way.
        let mut layout = TextLayout::<String>::from_text(data);
        layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
        layout.set_text_color(fill_color);
        layout.rebuild_if_needed(ctx.text(), env);

        // Let's rotate our text slightly. First we save our current (default) context:
        ctx.with_save(|ctx| {
            // Now we can rotate the context (or set a clip path, for instance):
            // This makes it so that anything drawn after this (in the closure) is
            // transformed.
            // The transformation is in radians, but be aware it transforms the canvas,
            // not just the part you are drawing. So we draw at (80.0, 40.0) on the rotated
            // canvas, this is NOT the same position as (80.0, 40.0) on the original canvas.
            ctx.transform(Affine::rotate(std::f64::consts::FRAC_PI_4));
            layout.draw(ctx, (80.0, 40.0));
        });
        // When we exit with_save, the original context's rotation is restored

        // This is the builder-style way of drawing text.
        let text = ctx.text();
        let layout = text
            .new_text_layout(data.clone())
            .font(FontFamily::SERIF, 24.0)
            .text_color(Color::rgb8(128, 0, 0))
            .build()
            .unwrap();
        ctx.draw_text(&layout, (100.0, 25.0));

        // Let's burn some CPU to make a (partially transparent) image buffer
        let image_data = make_image_data(256, 256);
        let image = ctx
            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
            .unwrap();
        // The image is automatically scaled to fit the rect you pass to draw_image
        ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear);
    }
source

pub fn paint_with_z_index( &mut self, z_index: u32, paint_func: impl FnOnce(&mut PaintCtx<'_, '_, '_>) + 'static )

Allows to specify order for paint operations.

Larger z_index indicate that an operation will be executed later.

Examples found in repository?
examples/anim.rs (lines 77-80)
74
75
76
77
78
79
80
81
82
83
    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), _env: &Env) {
        let t = self.t;
        let center = Point::new(50.0, 50.0);
        ctx.paint_with_z_index(1, move |ctx| {
            let ambit = center + 45.0 * Vec2::from_angle((0.75 + t) * 2.0 * PI);
            ctx.stroke(Line::new(center, ambit), &Color::WHITE, 1.0);
        });

        ctx.fill(Circle::new(center, 50.0), &Color::BLACK);
    }
More examples
Hide additional examples
examples/custom_widget.rs (lines 90-98)
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
    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
        // Clear the whole widget with the color of your choice
        // (ctx.size() returns the size of the layout rect we're painting in)
        // Note: ctx also has a `clear` method, but that clears the whole context,
        // and we only want to clear this widget's area.
        let size = ctx.size();
        let rect = size.to_rect();
        ctx.fill(rect, &Color::WHITE);

        // We can paint with a Z index, this indicates that this code will be run
        // after the rest of the painting. Painting with z-index is done in order,
        // so first everything with z-index 1 is painted and then with z-index 2 etc.
        // As you can see this(red) curve is drawn on top of the green curve
        ctx.paint_with_z_index(1, move |ctx| {
            let mut path = BezPath::new();
            path.move_to((0.0, size.height));
            path.quad_to((40.0, 50.0), (size.width, 0.0));
            // Create a color
            let stroke_color = Color::rgb8(128, 0, 0);
            // Stroke the path with thickness 1.0
            ctx.stroke(path, &stroke_color, 5.0);
        });

        // Create an arbitrary bezier path
        let mut path = BezPath::new();
        path.move_to(Point::ORIGIN);
        path.quad_to((40.0, 50.0), (size.width, size.height));
        // Create a color
        let stroke_color = Color::rgb8(0, 128, 0);
        // Stroke the path with thickness 5.0
        ctx.stroke(path, &stroke_color, 5.0);

        // Rectangles: the path for practical people
        let rect = Rect::from_origin_size((10.0, 10.0), (100.0, 100.0));
        // Note the Color:rgba8 which includes an alpha channel (7F in this case)
        let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
        ctx.fill(rect, &fill_color);

        // Text is easy; in real use TextLayout should either be stored in the
        // widget and reused, or a label child widget to manage it all.
        // This is one way of doing it, you can also use a builder-style way.
        let mut layout = TextLayout::<String>::from_text(data);
        layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
        layout.set_text_color(fill_color);
        layout.rebuild_if_needed(ctx.text(), env);

        // Let's rotate our text slightly. First we save our current (default) context:
        ctx.with_save(|ctx| {
            // Now we can rotate the context (or set a clip path, for instance):
            // This makes it so that anything drawn after this (in the closure) is
            // transformed.
            // The transformation is in radians, but be aware it transforms the canvas,
            // not just the part you are drawing. So we draw at (80.0, 40.0) on the rotated
            // canvas, this is NOT the same position as (80.0, 40.0) on the original canvas.
            ctx.transform(Affine::rotate(std::f64::consts::FRAC_PI_4));
            layout.draw(ctx, (80.0, 40.0));
        });
        // When we exit with_save, the original context's rotation is restored

        // This is the builder-style way of drawing text.
        let text = ctx.text();
        let layout = text
            .new_text_layout(data.clone())
            .font(FontFamily::SERIF, 24.0)
            .text_color(Color::rgb8(128, 0, 0))
            .build()
            .unwrap();
        ctx.draw_text(&layout, (100.0, 25.0));

        // Let's burn some CPU to make a (partially transparent) image buffer
        let image_data = make_image_data(256, 256);
        let image = ctx
            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
            .unwrap();
        // The image is automatically scaled to fit the rect you pass to draw_image
        ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear);
    }

Trait Implementations§

source§

impl<'c> Deref for PaintCtx<'_, '_, 'c>

§

type Target = CoreGraphicsContext<'c>

The resulting type after dereferencing.
source§

fn deref(&self) -> &Self::Target

Dereferences the value.
source§

impl<'c> DerefMut for PaintCtx<'_, '_, 'c>

source§

fn deref_mut(&mut self) -> &mut Self::Target

Mutably dereferences the value.

Auto Trait Implementations§

§

impl<'a, 'b, 'c> !RefUnwindSafe for PaintCtx<'a, 'b, 'c>

§

impl<'a, 'b, 'c> !Send for PaintCtx<'a, 'b, 'c>

§

impl<'a, 'b, 'c> !Sync for PaintCtx<'a, 'b, 'c>

§

impl<'a, 'b, 'c> Unpin for PaintCtx<'a, 'b, 'c>where 'b: 'a, 'c: 'a,

§

impl<'a, 'b, 'c> !UnwindSafe for PaintCtx<'a, 'b, 'c>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

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

fn in_current_span(self) -> Instrumented<Self>

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

impl<T, U> Into<U> for Twhere U: From<T>,

const: unstable · source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> RoundFrom<T> for T

§

fn round_from(x: T) -> T

Performs the conversion.
§

impl<T, U> RoundInto<U> for Twhere U: RoundFrom<T>,

§

fn round_into(self) -> U

Performs the conversion.
source§

impl<T> Same<T> for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
const: unstable · source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>where S: Into<Dispatch>,

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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