[go: up one dir, main page]

PaintCtx

Struct 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    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), env: &Env) {
275        let sz = ctx.size();
276
277        let monitors = Screen::get_monitors();
278        let all = monitors
279            .iter()
280            .map(|x| x.virtual_rect())
281            .fold(Rect::ZERO, |s, r| r.union(s));
282        if all.width() > 0. && all.height() > 0. {
283            let trans = Affine::scale(f64::min(sz.width / all.width(), sz.height / all.height()))
284                * Affine::translate(all.origin().to_vec2()).inverse();
285            let font = env.get(theme::UI_FONT).family;
286
287            for (i, mon) in monitors.iter().enumerate() {
288                let vr = mon.virtual_rect();
289                let tr = trans.transform_rect_bbox(vr);
290                ctx.stroke(tr, &Color::WHITE, 1.0);
291
292                if let Ok(tl) = ctx
293                    .text()
294                    .new_text_layout(format!(
295                        "{}:{}x{}@{},{}",
296                        i,
297                        vr.width(),
298                        vr.height(),
299                        vr.x0,
300                        vr.y0
301                    ))
302                    .max_width(tr.width() - 5.)
303                    .font(font.clone(), env.get(theme::TEXT_SIZE_NORMAL))
304                    .text_color(Color::WHITE)
305                    .build()
306                {
307                    ctx.draw_text(&tl, tr.center() - tl.size().to_vec2() * 0.5);
308                }
309            }
310        }
311    }
More examples
Hide additional examples
examples/custom_widget.rs (line 121)
77    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
78        // Clear the whole widget with the color of your choice
79        // (ctx.size() returns the size of the layout rect we're painting in)
80        // Note: ctx also has a `clear` method, but that clears the whole context,
81        // and we only want to clear this widget's area.
82        let size = ctx.size();
83        let rect = size.to_rect();
84        ctx.fill(rect, &Color::WHITE);
85
86        // We can paint with a Z index, this indicates that this code will be run
87        // after the rest of the painting. Painting with z-index is done in order,
88        // so first everything with z-index 1 is painted and then with z-index 2 etc.
89        // As you can see this(red) curve is drawn on top of the green curve
90        ctx.paint_with_z_index(1, move |ctx| {
91            let mut path = BezPath::new();
92            path.move_to((0.0, size.height));
93            path.quad_to((40.0, 50.0), (size.width, 0.0));
94            // Create a color
95            let stroke_color = Color::rgb8(128, 0, 0);
96            // Stroke the path with thickness 1.0
97            ctx.stroke(path, &stroke_color, 5.0);
98        });
99
100        // Create an arbitrary bezier path
101        let mut path = BezPath::new();
102        path.move_to(Point::ORIGIN);
103        path.quad_to((40.0, 50.0), (size.width, size.height));
104        // Create a color
105        let stroke_color = Color::rgb8(0, 128, 0);
106        // Stroke the path with thickness 5.0
107        ctx.stroke(path, &stroke_color, 5.0);
108
109        // Rectangles: the path for practical people
110        let rect = Rect::from_origin_size((10.0, 10.0), (100.0, 100.0));
111        // Note the Color:rgba8 which includes an alpha channel (7F in this case)
112        let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
113        ctx.fill(rect, &fill_color);
114
115        // Text is easy; in real use TextLayout should either be stored in the
116        // widget and reused, or a label child widget to manage it all.
117        // This is one way of doing it, you can also use a builder-style way.
118        let mut layout = TextLayout::<String>::from_text(data);
119        layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
120        layout.set_text_color(fill_color);
121        layout.rebuild_if_needed(ctx.text(), env);
122
123        // Let's rotate our text slightly. First we save our current (default) context:
124        ctx.with_save(|ctx| {
125            // Now we can rotate the context (or set a clip path, for instance):
126            // This makes it so that anything drawn after this (in the closure) is
127            // transformed.
128            // The transformation is in radians, but be aware it transforms the canvas,
129            // not just the part you are drawing. So we draw at (80.0, 40.0) on the rotated
130            // canvas, this is NOT the same position as (80.0, 40.0) on the original canvas.
131            ctx.transform(Affine::rotate(std::f64::consts::FRAC_PI_4));
132            layout.draw(ctx, (80.0, 40.0));
133        });
134        // When we exit with_save, the original context's rotation is restored
135
136        // This is the builder-style way of drawing text.
137        let text = ctx.text();
138        let layout = text
139            .new_text_layout(data.clone())
140            .font(FontFamily::SERIF, 24.0)
141            .text_color(Color::rgb8(128, 0, 0))
142            .build()
143            .unwrap();
144        ctx.draw_text(&layout, (100.0, 25.0));
145
146        // Let's burn some CPU to make a (partially transparent) image buffer
147        let image_data = make_image_data(256, 256);
148        let image = ctx
149            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
150            .unwrap();
151        // The image is automatically scaled to fit the rect you pass to draw_image
152        ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear);
153    }
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)
81fn make_ui() -> impl Widget<Color> {
82    Painter::new(|ctx, data, _env| {
83        let rect = ctx.size().to_rounded_rect(5.0);
84        ctx.fill(rect, data);
85    })
86    .fix_width(300.0)
87    .fix_height(300.0)
88    .padding(10.0)
89    .center()
90}
More examples
Hide additional examples
examples/scroll.rs (line 62)
61    fn paint(&mut self, ctx: &mut PaintCtx, _: &T, env: &Env) {
62        let rect = Rect::ZERO.with_size(ctx.size());
63        let color = env.get_debug_color(self.0);
64        let radius = (rect + INSETS).size().height / 2.0;
65        let circle = Circle::new(rect.center(), radius);
66        let grad = RadialGradient::new(1.0, (color, color.with_alpha(0.0)));
67        ctx.fill(circle, &grad);
68        ctx.stroke(rect, &color, 2.0);
69    }
examples/invalidation.rs (line 139)
137    fn paint(&mut self, ctx: &mut PaintCtx, data: &Vector<Circle>, _env: &Env) {
138        ctx.with_save(|ctx| {
139            let rect = ctx.size().to_rect();
140            ctx.clip(rect);
141            ctx.fill(rect, &Color::WHITE);
142            let now = Instant::now();
143            for c in data {
144                let color =
145                    Color::BLACK.with_alpha(now.duration_since(c.time).as_secs_f64().cos().abs());
146                ctx.fill(kurbo::Circle::new(c.pos, RADIUS), &color);
147            }
148        });
149    }
examples/calc.rs (line 123)
121fn op_button_label(op: char, label: String) -> impl Widget<CalcState> {
122    let painter = Painter::new(|ctx, _, env| {
123        let bounds = ctx.size().to_rect();
124
125        ctx.fill(bounds, &env.get(theme::PRIMARY_DARK));
126
127        if ctx.is_hot() {
128            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
129        }
130
131        if ctx.is_active() {
132            ctx.fill(bounds, &env.get(theme::PRIMARY_LIGHT));
133        }
134    });
135
136    Label::new(label)
137        .with_text_size(24.)
138        .center()
139        .background(painter)
140        .expand()
141        .on_click(move |_ctx, data: &mut CalcState, _env| data.op(op))
142}
143
144fn op_button(op: char) -> impl Widget<CalcState> {
145    op_button_label(op, op.to_string())
146}
147
148fn digit_button(digit: u8) -> impl Widget<CalcState> {
149    let painter = Painter::new(|ctx, _, env| {
150        let bounds = ctx.size().to_rect();
151
152        ctx.fill(bounds, &env.get(theme::BACKGROUND_LIGHT));
153
154        if ctx.is_hot() {
155            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
156        }
157
158        if ctx.is_active() {
159            ctx.fill(bounds, &Color::rgb8(0x71, 0x71, 0x71));
160        }
161    });
162
163    Label::new(format!("{digit}"))
164        .with_text_size(24.)
165        .center()
166        .background(painter)
167        .expand()
168        .on_click(move |_ctx, data: &mut CalcState, _env| data.digit(digit))
169}
examples/input_region.rs (line 171)
170    fn paint(&mut self, ctx: &mut druid::PaintCtx, data: &AppState, env: &druid::Env) {
171        let window_area = ctx.size();
172        let left_rect = Rect::new(0.0, 0.0, EXAMPLE_BORDER_SIZE, window_area.height);
173        let right_rect = Rect::new(
174            window_area.width - EXAMPLE_BORDER_SIZE,
175            0.0,
176            window_area.width,
177            window_area.height,
178        );
179        let bottom_rect = Rect::new(
180            0.0,
181            window_area.height - EXAMPLE_BORDER_SIZE,
182            window_area.width,
183            window_area.height,
184        );
185
186        ctx.fill(left_rect, &Color::rgba(1.0, 0., 0., 0.7));
187        ctx.fill(right_rect, &Color::rgba(1.0, 0., 0., 0.7));
188        ctx.fill(bottom_rect, &Color::rgba(1.0, 0., 0., 0.7));
189        self.info_label.paint(ctx, data, env);
190        self.controls.paint(ctx, data, env);
191    }
examples/game_of_life.rs (line 298)
297    fn paint(&mut self, ctx: &mut PaintCtx, data: &AppData, _env: &Env) {
298        let size: Size = ctx.size();
299        let w0 = size.width / GRID_SIZE as f64;
300        let h0 = size.height / GRID_SIZE as f64;
301        let cell_size = Size {
302            width: w0,
303            height: h0,
304        };
305        self.cell_size = cell_size;
306        for row in 0..GRID_SIZE {
307            for col in 0..GRID_SIZE {
308                let pos = GridPos { row, col };
309                if data.grid[pos] {
310                    let point = Point {
311                        x: w0 * row as f64,
312                        y: h0 * col as f64,
313                    };
314                    let rect = Rect::from_origin_size(point, cell_size);
315
316                    // We divide by 2 so that the colour changes every 2 positions instead of every 1
317                    ctx.fill(
318                        rect,
319                        &COLOURS[((pos.row * GRID_SIZE + pos.col) / 2) % COLOURS.len()],
320                    );
321                }
322            }
323        }
324    }
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    fn paint(&mut self, ctx: &mut PaintCtx, data: &State, env: &Env) {
120        if data.glow_hot && ctx.is_hot() {
121            BackgroundBrush::Color(Color::rgb8(200, 55, 55)).paint(ctx, data, env);
122        }
123        self.inner.paint(ctx, data, env);
124    }
More examples
Hide additional examples
examples/timer.rs (line 112)
111    fn paint(&mut self, ctx: &mut PaintCtx, data: &u32, env: &Env) {
112        let mut background = if ctx.is_hot() {
113            BackgroundBrush::Color(Color::rgb8(200, 55, 55))
114        } else {
115            BackgroundBrush::Color(Color::rgb8(30, 210, 170))
116        };
117        background.paint(ctx, data, env);
118    }
examples/calc.rs (line 127)
121fn op_button_label(op: char, label: String) -> impl Widget<CalcState> {
122    let painter = Painter::new(|ctx, _, env| {
123        let bounds = ctx.size().to_rect();
124
125        ctx.fill(bounds, &env.get(theme::PRIMARY_DARK));
126
127        if ctx.is_hot() {
128            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
129        }
130
131        if ctx.is_active() {
132            ctx.fill(bounds, &env.get(theme::PRIMARY_LIGHT));
133        }
134    });
135
136    Label::new(label)
137        .with_text_size(24.)
138        .center()
139        .background(painter)
140        .expand()
141        .on_click(move |_ctx, data: &mut CalcState, _env| data.op(op))
142}
143
144fn op_button(op: char) -> impl Widget<CalcState> {
145    op_button_label(op, op.to_string())
146}
147
148fn digit_button(digit: u8) -> impl Widget<CalcState> {
149    let painter = Painter::new(|ctx, _, env| {
150        let bounds = ctx.size().to_rect();
151
152        ctx.fill(bounds, &env.get(theme::BACKGROUND_LIGHT));
153
154        if ctx.is_hot() {
155            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
156        }
157
158        if ctx.is_active() {
159            ctx.fill(bounds, &Color::rgb8(0x71, 0x71, 0x71));
160        }
161    });
162
163    Label::new(format!("{digit}"))
164        .with_text_size(24.)
165        .center()
166        .background(painter)
167        .expand()
168        .on_click(move |_ctx, data: &mut CalcState, _env| data.digit(digit))
169}
examples/styled_text.rs (line 75)
72fn ui_builder() -> impl Widget<AppData> {
73    let my_painter = Painter::new(|ctx, _, _| {
74        let bounds = ctx.size().to_rect();
75        if ctx.is_hot() {
76            ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
77        }
78
79        if ctx.is_active() {
80            ctx.stroke(bounds, &Color::WHITE, 2.0);
81        }
82    });
83
84    // This is Druid's default text style.
85    // It's set by theme::LABEL_COLOR and theme::UI_FONT
86    let label =
87        Label::new(|data: &String, _env: &_| format!("Default: {data}")).lens(AppData::text);
88
89    // The text_color, text_size, and font builder methods can override the
90    // defaults provided by the theme by passing in a Key or a concrete value.
91    //
92    // In this example, text_color receives a Key from the theme, text_size
93    // gets a custom key which we set with the env_scope wrapper, and the
94    // default font key (theme::FONT_NAME) is overridden in the env_scope
95    // wrapper. (Like text_color and text_size, the font can be set using the
96    // with_font builder method, but overriding here makes it easy to fall back
97    // to the default font)
98    let styled_label = Label::new(|data: &AppData, _env: &_| format!("{data}"))
99        .with_text_color(theme::PRIMARY_LIGHT)
100        .with_font(MY_CUSTOM_FONT)
101        .background(my_painter)
102        .on_click(|_, data, _| {
103            data.size *= 1.1;
104        })
105        .env_scope(|env: &mut druid::Env, data: &AppData| {
106            let new_font = if data.mono {
107                FontDescriptor::new(FontFamily::MONOSPACE)
108            } else {
109                FontDescriptor::new(FontFamily::SYSTEM_UI)
110            }
111            .with_size(data.size);
112            env.set(MY_CUSTOM_FONT, new_font);
113        });
114
115    let labels = Scroll::new(
116        Flex::column()
117            .cross_axis_alignment(CrossAxisAlignment::Start)
118            .with_child(label)
119            .with_default_spacer()
120            .with_child(styled_label),
121    )
122    .expand_height()
123    .fix_width(COLUMN_WIDTH);
124
125    let stepper = Stepper::new()
126        .with_range(0.0, 100.0)
127        .with_step(1.0)
128        .with_wraparound(false)
129        .lens(AppData::size);
130
131    // TODO: Replace Parse usage with TextBox::with_formatter
132    #[allow(deprecated)]
133    let stepper_textbox = LensWrap::new(
134        Parse::new(TextBox::new()),
135        AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
136    );
137
138    let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
139    let stepper_row = Flex::row()
140        .with_child(stepper_textbox)
141        .with_child(stepper)
142        .with_default_spacer()
143        .with_child(mono_checkbox);
144
145    let input = TextBox::multiline()
146        .with_placeholder("Your sample text here :)")
147        .fix_width(COLUMN_WIDTH)
148        .fix_height(140.0)
149        .lens(AppData::text);
150
151    Flex::column()
152        .main_axis_alignment(MainAxisAlignment::Center)
153        .with_default_spacer()
154        .with_flex_child(labels, 1.0)
155        .with_default_spacer()
156        .with_child(input)
157        .with_default_spacer()
158        .with_child(stepper_row)
159        .with_default_spacer()
160}
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)
121fn op_button_label(op: char, label: String) -> impl Widget<CalcState> {
122    let painter = Painter::new(|ctx, _, env| {
123        let bounds = ctx.size().to_rect();
124
125        ctx.fill(bounds, &env.get(theme::PRIMARY_DARK));
126
127        if ctx.is_hot() {
128            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
129        }
130
131        if ctx.is_active() {
132            ctx.fill(bounds, &env.get(theme::PRIMARY_LIGHT));
133        }
134    });
135
136    Label::new(label)
137        .with_text_size(24.)
138        .center()
139        .background(painter)
140        .expand()
141        .on_click(move |_ctx, data: &mut CalcState, _env| data.op(op))
142}
143
144fn op_button(op: char) -> impl Widget<CalcState> {
145    op_button_label(op, op.to_string())
146}
147
148fn digit_button(digit: u8) -> impl Widget<CalcState> {
149    let painter = Painter::new(|ctx, _, env| {
150        let bounds = ctx.size().to_rect();
151
152        ctx.fill(bounds, &env.get(theme::BACKGROUND_LIGHT));
153
154        if ctx.is_hot() {
155            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
156        }
157
158        if ctx.is_active() {
159            ctx.fill(bounds, &Color::rgb8(0x71, 0x71, 0x71));
160        }
161    });
162
163    Label::new(format!("{digit}"))
164        .with_text_size(24.)
165        .center()
166        .background(painter)
167        .expand()
168        .on_click(move |_ctx, data: &mut CalcState, _env| data.digit(digit))
169}
More examples
Hide additional examples
examples/styled_text.rs (line 79)
72fn ui_builder() -> impl Widget<AppData> {
73    let my_painter = Painter::new(|ctx, _, _| {
74        let bounds = ctx.size().to_rect();
75        if ctx.is_hot() {
76            ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
77        }
78
79        if ctx.is_active() {
80            ctx.stroke(bounds, &Color::WHITE, 2.0);
81        }
82    });
83
84    // This is Druid's default text style.
85    // It's set by theme::LABEL_COLOR and theme::UI_FONT
86    let label =
87        Label::new(|data: &String, _env: &_| format!("Default: {data}")).lens(AppData::text);
88
89    // The text_color, text_size, and font builder methods can override the
90    // defaults provided by the theme by passing in a Key or a concrete value.
91    //
92    // In this example, text_color receives a Key from the theme, text_size
93    // gets a custom key which we set with the env_scope wrapper, and the
94    // default font key (theme::FONT_NAME) is overridden in the env_scope
95    // wrapper. (Like text_color and text_size, the font can be set using the
96    // with_font builder method, but overriding here makes it easy to fall back
97    // to the default font)
98    let styled_label = Label::new(|data: &AppData, _env: &_| format!("{data}"))
99        .with_text_color(theme::PRIMARY_LIGHT)
100        .with_font(MY_CUSTOM_FONT)
101        .background(my_painter)
102        .on_click(|_, data, _| {
103            data.size *= 1.1;
104        })
105        .env_scope(|env: &mut druid::Env, data: &AppData| {
106            let new_font = if data.mono {
107                FontDescriptor::new(FontFamily::MONOSPACE)
108            } else {
109                FontDescriptor::new(FontFamily::SYSTEM_UI)
110            }
111            .with_size(data.size);
112            env.set(MY_CUSTOM_FONT, new_font);
113        });
114
115    let labels = Scroll::new(
116        Flex::column()
117            .cross_axis_alignment(CrossAxisAlignment::Start)
118            .with_child(label)
119            .with_default_spacer()
120            .with_child(styled_label),
121    )
122    .expand_height()
123    .fix_width(COLUMN_WIDTH);
124
125    let stepper = Stepper::new()
126        .with_range(0.0, 100.0)
127        .with_step(1.0)
128        .with_wraparound(false)
129        .lens(AppData::size);
130
131    // TODO: Replace Parse usage with TextBox::with_formatter
132    #[allow(deprecated)]
133    let stepper_textbox = LensWrap::new(
134        Parse::new(TextBox::new()),
135        AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
136    );
137
138    let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
139    let stepper_row = Flex::row()
140        .with_child(stepper_textbox)
141        .with_child(stepper)
142        .with_default_spacer()
143        .with_child(mono_checkbox);
144
145    let input = TextBox::multiline()
146        .with_placeholder("Your sample text here :)")
147        .fix_width(COLUMN_WIDTH)
148        .fix_height(140.0)
149        .lens(AppData::text);
150
151    Flex::column()
152        .main_axis_alignment(MainAxisAlignment::Center)
153        .with_default_spacer()
154        .with_flex_child(labels, 1.0)
155        .with_default_spacer()
156        .with_child(input)
157        .with_default_spacer()
158        .with_child(stepper_row)
159        .with_default_spacer()
160}
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    fn paint(&mut self, ctx: &mut PaintCtx, data: &Vector<Circle>, _env: &Env) {
138        ctx.with_save(|ctx| {
139            let rect = ctx.size().to_rect();
140            ctx.clip(rect);
141            ctx.fill(rect, &Color::WHITE);
142            let now = Instant::now();
143            for c in data {
144                let color =
145                    Color::BLACK.with_alpha(now.duration_since(c.time).as_secs_f64().cos().abs());
146                ctx.fill(kurbo::Circle::new(c.pos, RADIUS), &color);
147            }
148        });
149    }
More examples
Hide additional examples
examples/custom_widget.rs (lines 124-133)
77    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
78        // Clear the whole widget with the color of your choice
79        // (ctx.size() returns the size of the layout rect we're painting in)
80        // Note: ctx also has a `clear` method, but that clears the whole context,
81        // and we only want to clear this widget's area.
82        let size = ctx.size();
83        let rect = size.to_rect();
84        ctx.fill(rect, &Color::WHITE);
85
86        // We can paint with a Z index, this indicates that this code will be run
87        // after the rest of the painting. Painting with z-index is done in order,
88        // so first everything with z-index 1 is painted and then with z-index 2 etc.
89        // As you can see this(red) curve is drawn on top of the green curve
90        ctx.paint_with_z_index(1, move |ctx| {
91            let mut path = BezPath::new();
92            path.move_to((0.0, size.height));
93            path.quad_to((40.0, 50.0), (size.width, 0.0));
94            // Create a color
95            let stroke_color = Color::rgb8(128, 0, 0);
96            // Stroke the path with thickness 1.0
97            ctx.stroke(path, &stroke_color, 5.0);
98        });
99
100        // Create an arbitrary bezier path
101        let mut path = BezPath::new();
102        path.move_to(Point::ORIGIN);
103        path.quad_to((40.0, 50.0), (size.width, size.height));
104        // Create a color
105        let stroke_color = Color::rgb8(0, 128, 0);
106        // Stroke the path with thickness 5.0
107        ctx.stroke(path, &stroke_color, 5.0);
108
109        // Rectangles: the path for practical people
110        let rect = Rect::from_origin_size((10.0, 10.0), (100.0, 100.0));
111        // Note the Color:rgba8 which includes an alpha channel (7F in this case)
112        let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
113        ctx.fill(rect, &fill_color);
114
115        // Text is easy; in real use TextLayout should either be stored in the
116        // widget and reused, or a label child widget to manage it all.
117        // This is one way of doing it, you can also use a builder-style way.
118        let mut layout = TextLayout::<String>::from_text(data);
119        layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
120        layout.set_text_color(fill_color);
121        layout.rebuild_if_needed(ctx.text(), env);
122
123        // Let's rotate our text slightly. First we save our current (default) context:
124        ctx.with_save(|ctx| {
125            // Now we can rotate the context (or set a clip path, for instance):
126            // This makes it so that anything drawn after this (in the closure) is
127            // transformed.
128            // The transformation is in radians, but be aware it transforms the canvas,
129            // not just the part you are drawing. So we draw at (80.0, 40.0) on the rotated
130            // canvas, this is NOT the same position as (80.0, 40.0) on the original canvas.
131            ctx.transform(Affine::rotate(std::f64::consts::FRAC_PI_4));
132            layout.draw(ctx, (80.0, 40.0));
133        });
134        // When we exit with_save, the original context's rotation is restored
135
136        // This is the builder-style way of drawing text.
137        let text = ctx.text();
138        let layout = text
139            .new_text_layout(data.clone())
140            .font(FontFamily::SERIF, 24.0)
141            .text_color(Color::rgb8(128, 0, 0))
142            .build()
143            .unwrap();
144        ctx.draw_text(&layout, (100.0, 25.0));
145
146        // Let's burn some CPU to make a (partially transparent) image buffer
147        let image_data = make_image_data(256, 256);
148        let image = ctx
149            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
150            .unwrap();
151        // The image is automatically scaled to fit the rect you pass to draw_image
152        ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear);
153    }
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    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), _env: &Env) {
75        let t = self.t;
76        let center = Point::new(50.0, 50.0);
77        ctx.paint_with_z_index(1, move |ctx| {
78            let ambit = center + 45.0 * Vec2::from_angle((0.75 + t) * 2.0 * PI);
79            ctx.stroke(Line::new(center, ambit), &Color::WHITE, 1.0);
80        });
81
82        ctx.fill(Circle::new(center, 50.0), &Color::BLACK);
83    }
More examples
Hide additional examples
examples/custom_widget.rs (lines 90-98)
77    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
78        // Clear the whole widget with the color of your choice
79        // (ctx.size() returns the size of the layout rect we're painting in)
80        // Note: ctx also has a `clear` method, but that clears the whole context,
81        // and we only want to clear this widget's area.
82        let size = ctx.size();
83        let rect = size.to_rect();
84        ctx.fill(rect, &Color::WHITE);
85
86        // We can paint with a Z index, this indicates that this code will be run
87        // after the rest of the painting. Painting with z-index is done in order,
88        // so first everything with z-index 1 is painted and then with z-index 2 etc.
89        // As you can see this(red) curve is drawn on top of the green curve
90        ctx.paint_with_z_index(1, move |ctx| {
91            let mut path = BezPath::new();
92            path.move_to((0.0, size.height));
93            path.quad_to((40.0, 50.0), (size.width, 0.0));
94            // Create a color
95            let stroke_color = Color::rgb8(128, 0, 0);
96            // Stroke the path with thickness 1.0
97            ctx.stroke(path, &stroke_color, 5.0);
98        });
99
100        // Create an arbitrary bezier path
101        let mut path = BezPath::new();
102        path.move_to(Point::ORIGIN);
103        path.quad_to((40.0, 50.0), (size.width, size.height));
104        // Create a color
105        let stroke_color = Color::rgb8(0, 128, 0);
106        // Stroke the path with thickness 5.0
107        ctx.stroke(path, &stroke_color, 5.0);
108
109        // Rectangles: the path for practical people
110        let rect = Rect::from_origin_size((10.0, 10.0), (100.0, 100.0));
111        // Note the Color:rgba8 which includes an alpha channel (7F in this case)
112        let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
113        ctx.fill(rect, &fill_color);
114
115        // Text is easy; in real use TextLayout should either be stored in the
116        // widget and reused, or a label child widget to manage it all.
117        // This is one way of doing it, you can also use a builder-style way.
118        let mut layout = TextLayout::<String>::from_text(data);
119        layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
120        layout.set_text_color(fill_color);
121        layout.rebuild_if_needed(ctx.text(), env);
122
123        // Let's rotate our text slightly. First we save our current (default) context:
124        ctx.with_save(|ctx| {
125            // Now we can rotate the context (or set a clip path, for instance):
126            // This makes it so that anything drawn after this (in the closure) is
127            // transformed.
128            // The transformation is in radians, but be aware it transforms the canvas,
129            // not just the part you are drawing. So we draw at (80.0, 40.0) on the rotated
130            // canvas, this is NOT the same position as (80.0, 40.0) on the original canvas.
131            ctx.transform(Affine::rotate(std::f64::consts::FRAC_PI_4));
132            layout.draw(ctx, (80.0, 40.0));
133        });
134        // When we exit with_save, the original context's rotation is restored
135
136        // This is the builder-style way of drawing text.
137        let text = ctx.text();
138        let layout = text
139            .new_text_layout(data.clone())
140            .font(FontFamily::SERIF, 24.0)
141            .text_color(Color::rgb8(128, 0, 0))
142            .build()
143            .unwrap();
144        ctx.draw_text(&layout, (100.0, 25.0));
145
146        // Let's burn some CPU to make a (partially transparent) image buffer
147        let image_data = make_image_data(256, 256);
148        let image = ctx
149            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
150            .unwrap();
151        // The image is automatically scaled to fit the rect you pass to draw_image
152        ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear);
153    }

Methods from Deref<Target = Piet<'c>>§

Source

pub fn assert_finished(&mut self)

Check whether drawing operations have finished.

Clients should call this before extracting or presenting the contents of the drawing surface.

Trait Implementations§

Source§

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

Source§

type Target = D2DRenderContext<'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> Freeze for PaintCtx<'a, 'b, 'c>

§

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>

§

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

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

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 T
where U: From<T>,

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.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> RoundFrom<T> for T

Source§

fn round_from(x: T) -> T

Performs the conversion.
Source§

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

Source§

fn round_into(self) -> U

Performs the conversion.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

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

Performs the conversion.
Source§

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

Source§

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

The type returned in the event of a conversion error.
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