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 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
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§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 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);
}
});
}
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))
}
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);
}
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()],
);
}
}
}
}
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
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))
}
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()
}
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?
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
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()
}
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 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
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);
}
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 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);
}