From 1d350ad15b64c0be7ee1c18ae89478636dba1ed8 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 29 Aug 2020 17:30:06 +0200 Subject: [PATCH] [painter] add helper functions for drawing lines, circles and rectangles --- egui/src/containers/resize.rs | 6 ++-- egui/src/containers/window.rs | 22 ++++++------- egui/src/demos/app.rs | 19 +++++++++++- egui/src/demos/fractal_clock.rs | 4 +-- egui/src/paint/command.rs | 36 ++++++--------------- egui/src/painter.rs | 55 ++++++++++++++++++++++++++++----- egui/src/ui.rs | 37 +++++++++------------- egui/src/widgets.rs | 20 +++++------- egui/src/widgets/text_edit.rs | 7 ++--- 9 files changed, 115 insertions(+), 91 deletions(-) diff --git a/egui/src/containers/resize.rs b/egui/src/containers/resize.rs index 001ba4a0..b3da44f5 100644 --- a/egui/src/containers/resize.rs +++ b/egui/src/containers/resize.rs @@ -279,10 +279,10 @@ pub fn paint_resize_corner_with_style(ui: &mut Ui, rect: &Rect, style: LineStyle let mut w = 2.0; while w < 12.0 { - painter.add(paint::PaintCmd::LineSegment { - points: [pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)], + painter.line_segment( + [pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)], style, - }); + ); w += 4.0; } } diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index 172a4935..9e88a1fc 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -672,10 +672,10 @@ impl TitleBar { let left = outer_rect.left(); let right = outer_rect.right(); let y = content_rect.top() + ui.style().item_spacing.y * 0.5; - ui.painter().add(PaintCmd::LineSegment { - points: [pos2(left, y), pos2(right, y)], - style: ui.style().interact.inactive.rect_outline.unwrap(), - }); + ui.painter().line_segment( + [pos2(left, y), pos2(right, y)], + ui.style().interact.inactive.rect_outline.unwrap(), + ); } 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_width = ui.style().interact(&interact).stroke_width; - ui.painter().add(PaintCmd::line_segment( + ui.painter().line_segment( [rect.left_top(), rect.right_bottom()], - stroke_width, - stroke_color, - )); - ui.painter().add(PaintCmd::line_segment( + (stroke_width, stroke_color), + ); + ui.painter().line_segment( [rect.right_top(), rect.left_bottom()], - stroke_width, - stroke_color, - )); + (stroke_width, stroke_color), + ); interact } diff --git a/egui/src/demos/app.rs b/egui/src/demos/app.rs index 8f6c9302..ade2c551 100644 --- a/egui/src/demos/app.rs +++ b/egui/src/demos/app.rs @@ -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 { // TODO: either show actual name clash, or remove this example 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.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)); }); if ui.add(Button::new("Assign PI")).clicked { diff --git a/egui/src/demos/fractal_clock.rs b/egui/src/demos/fractal_clock.rs index ef56d3b4..37178c50 100644 --- a/egui/src/demos/fractal_clock.rs +++ b/egui/src/demos/fractal_clock.rs @@ -1,6 +1,6 @@ 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", serde(default))] @@ -134,7 +134,7 @@ impl FractalClock { 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 = [ diff --git a/egui/src/paint/command.rs b/egui/src/paint/command.rs index 80af4d60..27cf2983 100644 --- a/egui/src/paint/command.rs +++ b/egui/src/paint/command.rs @@ -41,33 +41,6 @@ pub enum PaintCmd { Triangles(Triangles), } -impl PaintCmd { - pub fn line_segment(points: [Pos2; 2], width: f32, color: impl Into) -> Self { - Self::LineSegment { - points, - style: LineStyle::new(width, color), - } - } - - pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into) -> 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)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct LineStyle { @@ -83,3 +56,12 @@ impl LineStyle { } } } + +impl From<(f32, Color)> for LineStyle +where + Color: Into, +{ + fn from((width, color): (f32, Color)) -> LineStyle { + LineStyle::new(width, color) + } +} diff --git a/egui/src/painter.rs b/egui/src/painter.rs index 1a2e3d3a..b8283146 100644 --- a/egui/src/painter.rs +++ b/egui/src/painter.rs @@ -115,12 +115,7 @@ impl Painter { /// ## Debug painting impl Painter { pub fn debug_rect(&mut self, rect: Rect, color: Srgba, text: impl Into) { - self.add(PaintCmd::Rect { - corner_radius: 0.0, - fill: None, - outline: Some(LineStyle::new(1.0, color)), - rect, - }); + self.rect_outline(rect, 0.0, (1.0, color)); let anchor = (Align::Min, Align::Min); let text_style = TextStyle::Monospace; 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 rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor); self.add(PaintCmd::Rect { + rect: rect.expand(2.0), corner_radius: 0.0, fill: Some(Srgba::black_alpha(240)), outline: Some(LineStyle::new(1.0, color::RED)), - rect: rect.expand(2.0), }); 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) { + self.add(PaintCmd::LineSegment { + points, + style: style.into(), + }); + } + + pub fn circle_filled(&self, center: Pos2, radius: f32, fill_color: impl Into) { + 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) { + 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) { + 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) { + self.add(PaintCmd::Rect { + rect, + corner_radius, + fill: None, + outline: Some(outline.into()), + }); + } +} + /// ## Text impl Painter { /// Lay out and paint some text. diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 1b2d37d3..846eaaa8 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -391,20 +391,12 @@ impl Ui { let rect = self.reserve_space_impl(desired_size); if self.style().debug_widget_rects { - self.painter.add(PaintCmd::Rect { - rect, - corner_radius: 0.0, - outline: Some(LineStyle::new(1.0, LIGHT_BLUE)), - fill: None, - }); + self.painter.rect_outline(rect, 0.0, (1.0, LIGHT_BLUE)); let color = color::srgba(200, 0, 0, 255); let width = 2.5; - let paint_line_seg = |a, b| { - self.painter - .add(PaintCmd::line_segment([a, b], width, color)) - }; + let paint_line_seg = |a, b| self.painter().line_segment([a, b], (width, color)); if too_wide { paint_line_seg(rect.left_top(), rect.left_bottom()); @@ -433,15 +425,6 @@ impl Ui { self.child_count += 1; 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 @@ -522,6 +505,15 @@ impl Ui { 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: @@ -581,11 +573,10 @@ impl Ui { let line_start = child_rect.min - indent * 0.5; 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); - self.painter.add(PaintCmd::line_segment( + self.painter.line_segment( [line_start, line_end], - self.style.line_width, - Srgba::gray(150), - )); + (self.style.line_width, Srgba::gray(150)), + ); (ret, self.allocate_space(indent + size)) } diff --git a/egui/src/widgets.rs b/egui/src/widgets.rs index 6e00d11e..963b7cca 100644 --- a/egui/src/widgets.rs +++ b/egui/src/widgets.rs @@ -199,11 +199,10 @@ impl Widget for Hyperlink { let y = ui.painter().round_to_pixel(y); let min_x = pos.x + line.min_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)], - ui.style().line_width, - color, - )); + (ui.style().line_width, 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 bg_fill = fill.or(ui.style().interact(&interact).bg_fill); ui.painter().add(PaintCmd::Rect { + rect: interact.rect, corner_radius: ui.style().interact(&interact).corner_radius, fill: bg_fill, outline: ui.style().interact(&interact).rect_outline, - rect: interact.rect, }); let stroke_color = ui.style().interact(&interact).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); ui.painter().add(PaintCmd::Rect { + rect: big_icon_rect, corner_radius: ui.style().interact(&interact).corner_radius, fill: ui.style().interact(&interact).bg_fill, outline: ui.style().interact(&interact).rect_outline, - rect: big_icon_rect, }); let stroke_color = ui.style().interact(&interact).stroke_color; @@ -436,17 +435,17 @@ impl Widget for RadioButton { painter.add(PaintCmd::Circle { center: big_icon_rect.center(), + radius: big_icon_rect.width() / 2.0, fill: bg_fill, outline: ui.style().interact(&interact).rect_outline, // TODO - radius: big_icon_rect.width() / 2.0, }); if checked { painter.add(PaintCmd::Circle { center: small_icon_rect.center(), + radius: small_icon_rect.width() / 3.0, fill: Some(stroke_color), outline: None, - radius: small_icon_rect.width() / 3.0, }); } @@ -538,10 +537,7 @@ impl Widget for Separator { ) } }; - ui.painter().add(PaintCmd::LineSegment { - points, - style: LineStyle::new(line_width, color), - }); + ui.painter().line_segment(points, (line_width, color)); ui.interact_hover(rect) } } diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index 1045c6c7..4eedefe9 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -200,11 +200,10 @@ impl<'t> Widget for TextEdit<'t> { if show_cursor { if let Some(cursor) = state.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)], - ui.style().text_cursor_width, - color::WHITE, - )); + (ui.style().text_cursor_width, color::WHITE), + ); } } }