[painter] add helper functions for drawing lines, circles and rectangles
This commit is contained in:
parent
c27e53a7b2
commit
1d350ad15b
9 changed files with 115 additions and 91 deletions
|
@ -279,10 +279,10 @@ pub fn paint_resize_corner_with_style(ui: &mut Ui, rect: &Rect, style: LineStyle
|
||||||
let mut w = 2.0;
|
let mut w = 2.0;
|
||||||
|
|
||||||
while w < 12.0 {
|
while w < 12.0 {
|
||||||
painter.add(paint::PaintCmd::LineSegment {
|
painter.line_segment(
|
||||||
points: [pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)],
|
[pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)],
|
||||||
style,
|
style,
|
||||||
});
|
);
|
||||||
w += 4.0;
|
w += 4.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -672,10 +672,10 @@ impl TitleBar {
|
||||||
let left = outer_rect.left();
|
let left = outer_rect.left();
|
||||||
let right = outer_rect.right();
|
let right = outer_rect.right();
|
||||||
let y = content_rect.top() + ui.style().item_spacing.y * 0.5;
|
let y = content_rect.top() + ui.style().item_spacing.y * 0.5;
|
||||||
ui.painter().add(PaintCmd::LineSegment {
|
ui.painter().line_segment(
|
||||||
points: [pos2(left, y), pos2(right, y)],
|
[pos2(left, y), pos2(right, y)],
|
||||||
style: ui.style().interact.inactive.rect_outline.unwrap(),
|
ui.style().interact.inactive.rect_outline.unwrap(),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let title_bar_id = ui.make_child_id("title_bar");
|
let title_bar_id = ui.make_child_id("title_bar");
|
||||||
|
@ -709,15 +709,13 @@ fn close_button(ui: &mut Ui, rect: Rect) -> InteractInfo {
|
||||||
|
|
||||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||||
let stroke_width = ui.style().interact(&interact).stroke_width;
|
let stroke_width = ui.style().interact(&interact).stroke_width;
|
||||||
ui.painter().add(PaintCmd::line_segment(
|
ui.painter().line_segment(
|
||||||
[rect.left_top(), rect.right_bottom()],
|
[rect.left_top(), rect.right_bottom()],
|
||||||
stroke_width,
|
(stroke_width, stroke_color),
|
||||||
stroke_color,
|
);
|
||||||
));
|
ui.painter().line_segment(
|
||||||
ui.painter().add(PaintCmd::line_segment(
|
|
||||||
[rect.right_top(), rect.left_bottom()],
|
[rect.right_top(), rect.left_bottom()],
|
||||||
stroke_width,
|
(stroke_width, stroke_color),
|
||||||
stroke_color,
|
);
|
||||||
));
|
|
||||||
interact
|
interact
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,6 +394,23 @@ impl DemoWindow {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CollapsingHeader::new("Misc")
|
||||||
|
.default_open(false)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("You can pretty easily paint your own small icons:");
|
||||||
|
let painter = ui.canvas(Vec2::splat(16.0));
|
||||||
|
let c = painter.clip_rect().center();
|
||||||
|
let r = painter.clip_rect().width() / 2.0 - 1.0;
|
||||||
|
let color = Srgba::gray(128);
|
||||||
|
let line_style = LineStyle::new(1.0, color);
|
||||||
|
painter.circle_outline(c, r, line_style);
|
||||||
|
painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], line_style);
|
||||||
|
painter.line_segment([c, c + r * Vec2::angled(TAU * 1.0 / 8.0)], line_style);
|
||||||
|
painter.line_segment([c, c + r * Vec2::angled(TAU * 3.0 / 8.0)], line_style);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
// TODO: either show actual name clash, or remove this example
|
// TODO: either show actual name clash, or remove this example
|
||||||
ui.collapsing("Name clash demo", |ui| {
|
ui.collapsing("Name clash demo", |ui| {
|
||||||
|
@ -490,7 +507,7 @@ impl Widgets {
|
||||||
);
|
);
|
||||||
ui.add(Slider::f32(&mut self.slider_value, -10.0..=10.0).text("value"));
|
ui.add(Slider::f32(&mut self.slider_value, -10.0..=10.0).text("value"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("drag this number:");
|
ui.label("More compact as a value you drag:");
|
||||||
ui.add(DragValue::f32(&mut self.slider_value).speed(0.01));
|
ui.add(DragValue::f32(&mut self.slider_value).speed(0.01));
|
||||||
});
|
});
|
||||||
if ui.add(Button::new("Assign PI")).clicked {
|
if ui.add(Button::new("Assign PI")).clicked {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{containers::*, paint::PaintCmd, widgets::*, *};
|
use crate::{containers::*, widgets::*, *};
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
|
@ -134,7 +134,7 @@ impl FractalClock {
|
||||||
rect.center() + scale * points[1].to_vec2(),
|
rect.center() + scale * points[1].to_vec2(),
|
||||||
];
|
];
|
||||||
|
|
||||||
painter.add(PaintCmd::line_segment([line[0], line[1]], width, color));
|
painter.line_segment([line[0], line[1]], (width, color));
|
||||||
};
|
};
|
||||||
|
|
||||||
let hand_rotations = [
|
let hand_rotations = [
|
||||||
|
|
|
@ -41,33 +41,6 @@ pub enum PaintCmd {
|
||||||
Triangles(Triangles),
|
Triangles(Triangles),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintCmd {
|
|
||||||
pub fn line_segment(points: [Pos2; 2], width: f32, color: impl Into<Srgba>) -> Self {
|
|
||||||
Self::LineSegment {
|
|
||||||
points,
|
|
||||||
style: LineStyle::new(width, color),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into<Srgba>) -> Self {
|
|
||||||
Self::Circle {
|
|
||||||
center,
|
|
||||||
radius,
|
|
||||||
fill: Some(fill_color.into()),
|
|
||||||
outline: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn circle_outline(center: Pos2, radius: f32, outline: LineStyle) -> Self {
|
|
||||||
Self::Circle {
|
|
||||||
center,
|
|
||||||
radius,
|
|
||||||
fill: None,
|
|
||||||
outline: Some(outline),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct LineStyle {
|
pub struct LineStyle {
|
||||||
|
@ -83,3 +56,12 @@ impl LineStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Color> From<(f32, Color)> for LineStyle
|
||||||
|
where
|
||||||
|
Color: Into<Srgba>,
|
||||||
|
{
|
||||||
|
fn from((width, color): (f32, Color)) -> LineStyle {
|
||||||
|
LineStyle::new(width, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -115,12 +115,7 @@ impl Painter {
|
||||||
/// ## Debug painting
|
/// ## Debug painting
|
||||||
impl Painter {
|
impl Painter {
|
||||||
pub fn debug_rect(&mut self, rect: Rect, color: Srgba, text: impl Into<String>) {
|
pub fn debug_rect(&mut self, rect: Rect, color: Srgba, text: impl Into<String>) {
|
||||||
self.add(PaintCmd::Rect {
|
self.rect_outline(rect, 0.0, (1.0, color));
|
||||||
corner_radius: 0.0,
|
|
||||||
fill: None,
|
|
||||||
outline: Some(LineStyle::new(1.0, color)),
|
|
||||||
rect,
|
|
||||||
});
|
|
||||||
let anchor = (Align::Min, Align::Min);
|
let anchor = (Align::Min, Align::Min);
|
||||||
let text_style = TextStyle::Monospace;
|
let text_style = TextStyle::Monospace;
|
||||||
self.text(rect.min, anchor, text.into(), text_style, color);
|
self.text(rect.min, anchor, text.into(), text_style, color);
|
||||||
|
@ -134,15 +129,61 @@ impl Painter {
|
||||||
let galley = font.layout_multiline(text, f32::INFINITY);
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor);
|
let rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor);
|
||||||
self.add(PaintCmd::Rect {
|
self.add(PaintCmd::Rect {
|
||||||
|
rect: rect.expand(2.0),
|
||||||
corner_radius: 0.0,
|
corner_radius: 0.0,
|
||||||
fill: Some(Srgba::black_alpha(240)),
|
fill: Some(Srgba::black_alpha(240)),
|
||||||
outline: Some(LineStyle::new(1.0, color::RED)),
|
outline: Some(LineStyle::new(1.0, color::RED)),
|
||||||
rect: rect.expand(2.0),
|
|
||||||
});
|
});
|
||||||
self.galley(rect.min, galley, text_style, color::RED);
|
self.galley(rect.min, galley, text_style, color::RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Paint different primitives
|
||||||
|
impl Painter {
|
||||||
|
pub fn line_segment(&self, points: [Pos2; 2], style: impl Into<LineStyle>) {
|
||||||
|
self.add(PaintCmd::LineSegment {
|
||||||
|
points,
|
||||||
|
style: style.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn circle_filled(&self, center: Pos2, radius: f32, fill_color: impl Into<Srgba>) {
|
||||||
|
self.add(PaintCmd::Circle {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
fill: Some(fill_color.into()),
|
||||||
|
outline: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn circle_outline(&self, center: Pos2, radius: f32, outline: impl Into<LineStyle>) {
|
||||||
|
self.add(PaintCmd::Circle {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
fill: None,
|
||||||
|
outline: Some(outline.into()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rect_filled(&self, rect: Rect, corner_radius: f32, fill_color: impl Into<Srgba>) {
|
||||||
|
self.add(PaintCmd::Rect {
|
||||||
|
rect,
|
||||||
|
corner_radius,
|
||||||
|
fill: Some(fill_color.into()),
|
||||||
|
outline: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rect_outline(&self, rect: Rect, corner_radius: f32, outline: impl Into<LineStyle>) {
|
||||||
|
self.add(PaintCmd::Rect {
|
||||||
|
rect,
|
||||||
|
corner_radius,
|
||||||
|
fill: None,
|
||||||
|
outline: Some(outline.into()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ## Text
|
/// ## Text
|
||||||
impl Painter {
|
impl Painter {
|
||||||
/// Lay out and paint some text.
|
/// Lay out and paint some text.
|
||||||
|
|
|
@ -391,20 +391,12 @@ impl Ui {
|
||||||
let rect = self.reserve_space_impl(desired_size);
|
let rect = self.reserve_space_impl(desired_size);
|
||||||
|
|
||||||
if self.style().debug_widget_rects {
|
if self.style().debug_widget_rects {
|
||||||
self.painter.add(PaintCmd::Rect {
|
self.painter.rect_outline(rect, 0.0, (1.0, LIGHT_BLUE));
|
||||||
rect,
|
|
||||||
corner_radius: 0.0,
|
|
||||||
outline: Some(LineStyle::new(1.0, LIGHT_BLUE)),
|
|
||||||
fill: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let color = color::srgba(200, 0, 0, 255);
|
let color = color::srgba(200, 0, 0, 255);
|
||||||
let width = 2.5;
|
let width = 2.5;
|
||||||
|
|
||||||
let paint_line_seg = |a, b| {
|
let paint_line_seg = |a, b| self.painter().line_segment([a, b], (width, color));
|
||||||
self.painter
|
|
||||||
.add(PaintCmd::line_segment([a, b], width, color))
|
|
||||||
};
|
|
||||||
|
|
||||||
if too_wide {
|
if too_wide {
|
||||||
paint_line_seg(rect.left_top(), rect.left_bottom());
|
paint_line_seg(rect.left_top(), rect.left_bottom());
|
||||||
|
@ -433,15 +425,6 @@ impl Ui {
|
||||||
self.child_count += 1;
|
self.child_count += 1;
|
||||||
child_rect
|
child_rect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ask to allocate a certain amount of space and return a Painter for that region.
|
|
||||||
///
|
|
||||||
/// You may get back a `Painter` with a smaller or larger size than what you desired,
|
|
||||||
/// depending on the available space and the current layout.
|
|
||||||
pub fn allocate_canvas(&mut self, desired_size: Vec2) -> Painter {
|
|
||||||
let rect = self.allocate_space(desired_size);
|
|
||||||
self.painter_at(rect)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Adding widgets
|
/// # Adding widgets
|
||||||
|
@ -522,6 +505,15 @@ impl Ui {
|
||||||
|
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ask to allocate a certain amount of space and return a Painter for that region.
|
||||||
|
///
|
||||||
|
/// You may get back a `Painter` with a smaller or larger size than what you desired,
|
||||||
|
/// depending on the available space and the current layout.
|
||||||
|
pub fn canvas(&mut self, desired_size: Vec2) -> Painter {
|
||||||
|
let rect = self.allocate_space(desired_size);
|
||||||
|
self.painter_at(rect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Adding Containers / Sub-uis:
|
/// # Adding Containers / Sub-uis:
|
||||||
|
@ -581,11 +573,10 @@ impl Ui {
|
||||||
let line_start = child_rect.min - indent * 0.5;
|
let line_start = child_rect.min - indent * 0.5;
|
||||||
let line_start = self.painter().round_pos_to_pixels(line_start);
|
let line_start = self.painter().round_pos_to_pixels(line_start);
|
||||||
let line_end = pos2(line_start.x, line_start.y + size.y - 2.0);
|
let line_end = pos2(line_start.x, line_start.y + size.y - 2.0);
|
||||||
self.painter.add(PaintCmd::line_segment(
|
self.painter.line_segment(
|
||||||
[line_start, line_end],
|
[line_start, line_end],
|
||||||
self.style.line_width,
|
(self.style.line_width, Srgba::gray(150)),
|
||||||
Srgba::gray(150),
|
);
|
||||||
));
|
|
||||||
|
|
||||||
(ret, self.allocate_space(indent + size))
|
(ret, self.allocate_space(indent + size))
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,11 +199,10 @@ impl Widget for Hyperlink {
|
||||||
let y = ui.painter().round_to_pixel(y);
|
let y = ui.painter().round_to_pixel(y);
|
||||||
let min_x = pos.x + line.min_x();
|
let min_x = pos.x + line.min_x();
|
||||||
let max_x = pos.x + line.max_x();
|
let max_x = pos.x + line.max_x();
|
||||||
ui.painter().add(PaintCmd::line_segment(
|
ui.painter().line_segment(
|
||||||
[pos2(min_x, y), pos2(max_x, y)],
|
[pos2(min_x, y), pos2(max_x, y)],
|
||||||
ui.style().line_width,
|
(ui.style().line_width, color),
|
||||||
color,
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,10 +289,10 @@ impl Widget for Button {
|
||||||
let text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
|
let text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
|
||||||
let bg_fill = fill.or(ui.style().interact(&interact).bg_fill);
|
let bg_fill = fill.or(ui.style().interact(&interact).bg_fill);
|
||||||
ui.painter().add(PaintCmd::Rect {
|
ui.painter().add(PaintCmd::Rect {
|
||||||
|
rect: interact.rect,
|
||||||
corner_radius: ui.style().interact(&interact).corner_radius,
|
corner_radius: ui.style().interact(&interact).corner_radius,
|
||||||
fill: bg_fill,
|
fill: bg_fill,
|
||||||
outline: ui.style().interact(&interact).rect_outline,
|
outline: ui.style().interact(&interact).rect_outline,
|
||||||
rect: interact.rect,
|
|
||||||
});
|
});
|
||||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||||
let text_color = text_color.unwrap_or(stroke_color);
|
let text_color = text_color.unwrap_or(stroke_color);
|
||||||
|
@ -354,10 +353,10 @@ impl<'a> Widget for Checkbox<'a> {
|
||||||
}
|
}
|
||||||
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
|
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
|
||||||
ui.painter().add(PaintCmd::Rect {
|
ui.painter().add(PaintCmd::Rect {
|
||||||
|
rect: big_icon_rect,
|
||||||
corner_radius: ui.style().interact(&interact).corner_radius,
|
corner_radius: ui.style().interact(&interact).corner_radius,
|
||||||
fill: ui.style().interact(&interact).bg_fill,
|
fill: ui.style().interact(&interact).bg_fill,
|
||||||
outline: ui.style().interact(&interact).rect_outline,
|
outline: ui.style().interact(&interact).rect_outline,
|
||||||
rect: big_icon_rect,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||||
|
@ -436,17 +435,17 @@ impl Widget for RadioButton {
|
||||||
|
|
||||||
painter.add(PaintCmd::Circle {
|
painter.add(PaintCmd::Circle {
|
||||||
center: big_icon_rect.center(),
|
center: big_icon_rect.center(),
|
||||||
|
radius: big_icon_rect.width() / 2.0,
|
||||||
fill: bg_fill,
|
fill: bg_fill,
|
||||||
outline: ui.style().interact(&interact).rect_outline, // TODO
|
outline: ui.style().interact(&interact).rect_outline, // TODO
|
||||||
radius: big_icon_rect.width() / 2.0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if checked {
|
if checked {
|
||||||
painter.add(PaintCmd::Circle {
|
painter.add(PaintCmd::Circle {
|
||||||
center: small_icon_rect.center(),
|
center: small_icon_rect.center(),
|
||||||
|
radius: small_icon_rect.width() / 3.0,
|
||||||
fill: Some(stroke_color),
|
fill: Some(stroke_color),
|
||||||
outline: None,
|
outline: None,
|
||||||
radius: small_icon_rect.width() / 3.0,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,10 +537,7 @@ impl Widget for Separator {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ui.painter().add(PaintCmd::LineSegment {
|
ui.painter().line_segment(points, (line_width, color));
|
||||||
points,
|
|
||||||
style: LineStyle::new(line_width, color),
|
|
||||||
});
|
|
||||||
ui.interact_hover(rect)
|
ui.interact_hover(rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,11 +200,10 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
if show_cursor {
|
if show_cursor {
|
||||||
if let Some(cursor) = state.cursor {
|
if let Some(cursor) = state.cursor {
|
||||||
let cursor_pos = interact.rect.min + galley.char_start_pos(cursor);
|
let cursor_pos = interact.rect.min + galley.char_start_pos(cursor);
|
||||||
painter.add(PaintCmd::line_segment(
|
painter.line_segment(
|
||||||
[cursor_pos, cursor_pos + vec2(0.0, line_spacing)],
|
[cursor_pos, cursor_pos + vec2(0.0, line_spacing)],
|
||||||
ui.style().text_cursor_width,
|
(ui.style().text_cursor_width, color::WHITE),
|
||||||
color::WHITE,
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue