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<'_, '_, '_>
impl PaintCtx<'_, '_, '_>
Sourcepub fn window(&self) -> &WindowHandle
pub fn window(&self) -> &WindowHandle
Returns a reference to the current WindowHandle.
Sourcepub fn text(&mut self) -> &mut PietText
pub fn text(&mut self) -> &mut PietText
Get an object which can create text layouts.
Examples found in repository?
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
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§impl PaintCtx<'_, '_, '_>
impl PaintCtx<'_, '_, '_>
Sourcepub fn size(&self) -> Size
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?
More examples
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 }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}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 }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 }Sourcepub fn window_origin(&self) -> Point
pub fn window_origin(&self) -> Point
The origin of the widget in window coordinates, relative to the top left corner of the content area.
Sourcepub fn to_window(&self, widget_point: Point) -> Point
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.
Sourcepub fn to_screen(&self, widget_point: Point) -> Point
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
Sourcepub fn is_hot(&self) -> bool
pub fn is_hot(&self) -> bool
Query the “hot” state of the widget.
See WidgetPod::is_hot for additional information.
Examples found in repository?
More examples
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}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}Sourcepub fn is_active(&self) -> bool
pub fn is_active(&self) -> bool
Query the “active” state of the widget.
See WidgetPod::is_active for additional information.
Examples found in repository?
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
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}Sourcepub fn is_focused(&self) -> bool
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.
Sourcepub fn has_focus(&self) -> bool
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,
Sourcepub fn is_disabled(&self) -> bool
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<'_, '_, '_>
impl PaintCtx<'_, '_, '_>
Sourcepub fn depth(&self) -> u32
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.
Sourcepub fn with_child_ctx(
&mut self,
region: impl Into<Region>,
f: impl FnOnce(&mut PaintCtx<'_, '_, '_>),
)
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.
Sourcepub fn with_save(&mut self, f: impl FnOnce(&mut PaintCtx<'_, '_, '_>))
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?
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
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 }Sourcepub fn paint_with_z_index(
&mut self,
z_index: u32,
paint_func: impl FnOnce(&mut PaintCtx<'_, '_, '_>) + 'static,
)
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?
More examples
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>>§
Sourcepub fn assert_finished(&mut self)
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.