diff --git a/egui/src/containers/collapsing_header.rs b/egui/src/containers/collapsing_header.rs index 7ce666e8..56c7f94d 100644 --- a/egui/src/containers/collapsing_header.rs +++ b/egui/src/containers/collapsing_header.rs @@ -235,7 +235,7 @@ impl CollapsingHeader { } let painter = ui.painter(); - painter.add_galley( + painter.galley( text_pos, galley, label.text_style, diff --git a/egui/src/context.rs b/egui/src/context.rs index 637fbd5f..93e3b2f1 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -125,14 +125,17 @@ impl Context { (point * self.input.pixels_per_point).round() / self.input.pixels_per_point } + /// Useful for pixel-perfect rendering pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 { pos2(self.round_to_pixel(pos.x), self.round_to_pixel(pos.y)) } + /// Useful for pixel-perfect rendering pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 { vec2(self.round_to_pixel(vec.x), self.round_to_pixel(vec.y)) } + /// Useful for pixel-perfect rendering pub fn round_rect_to_pixels(&self, rect: Rect) -> Rect { Rect { min: self.round_pos_to_pixels(rect.min), diff --git a/egui/src/layout.rs b/egui/src/layout.rs index 95ef1d23..a045da77 100644 --- a/egui/src/layout.rs +++ b/egui/src/layout.rs @@ -39,13 +39,13 @@ impl Default for Align { /// Used e.g. to anchor a piece of text to a part of the rectangle. /// Give a position within the rect, specified by the aligns -pub(crate) fn align_rect(rect: Rect, align: (Align, Align)) -> Rect { - let x = match align.0 { +pub(crate) fn anchor_rect(rect: Rect, anchor: (Align, Align)) -> Rect { + let x = match anchor.0 { Align::Min => rect.left(), Align::Center => rect.left() - 0.5 * rect.width(), Align::Max => rect.left() - rect.width(), }; - let y = match align.1 { + let y = match anchor.1 { Align::Min => rect.top(), Align::Center => rect.top() - 0.5 * rect.height(), Align::Max => rect.top() - rect.height(), diff --git a/egui/src/math.rs b/egui/src/math.rs index 91f70800..d94b252c 100644 --- a/egui/src/math.rs +++ b/egui/src/math.rs @@ -21,6 +21,12 @@ impl From<[f32; 2]> for Vec2 { } } +impl From<&[f32; 2]> for Vec2 { + fn from(v: &[f32; 2]) -> Self { + Self { x: v[0], y: v[1] } + } +} + impl Vec2 { pub fn zero() -> Self { Self { x: 0.0, y: 0.0 } @@ -245,7 +251,17 @@ impl From<[f32; 2]> for Pos2 { } } +impl From<&[f32; 2]> for Pos2 { + fn from(v: &[f32; 2]) -> Self { + Self { x: v[0], y: v[1] } + } +} + impl Pos2 { + pub fn new(x: f32, y: f32) -> Self { + Self { x, y } + } + pub fn to_vec2(self) -> Vec2 { Vec2 { x: self.x, @@ -492,6 +508,9 @@ impl Rect { pub fn height(&self) -> f32 { self.max.y - self.min.y } + pub fn area(&self) -> f32 { + self.width() * self.height() + } pub fn range_x(&self) -> RangeInclusive { self.min.x..=self.max.x diff --git a/egui/src/paint/command.rs b/egui/src/paint/command.rs index 9186c95c..63042a44 100644 --- a/egui/src/paint/command.rs +++ b/egui/src/paint/command.rs @@ -10,9 +10,9 @@ pub enum PaintCmd { Noop, Circle { center: Pos2, + radius: f32, fill: Option, outline: Option, - radius: f32, }, LineSegment { points: [Pos2; 2], @@ -48,6 +48,24 @@ impl PaintCmd { style: LineStyle::new(width, color), } } + + pub fn circle_filled(center: Pos2, radius: f32, fill_color: Color) -> Self { + Self::Circle { + center, + radius, + fill: Some(fill_color), + 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)] diff --git a/egui/src/painter.rs b/egui/src/painter.rs index 33b5aafe..1089600f 100644 --- a/egui/src/painter.rs +++ b/egui/src/painter.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::{ - align_rect, color, + anchor_rect, color, layers::PaintCmdIdx, math::{Pos2, Rect, Vec2}, paint::{font, Fonts, LineStyle, PaintCmd, TextStyle}, @@ -30,16 +30,24 @@ impl Painter { clip_rect, } } + + /// Create a painter for a sub-region of this `Painter`. + /// + /// The clip-rect of the returned `Painter` will be the intersection + /// of the given rectangle and the `clip_rect()` of this `Painter`. + pub fn sub_region(&self, rect: Rect) -> Self { + Self::new(self.ctx.clone(), self.layer, rect.intersect(self.clip_rect)) + } } /// ## Accessors etc impl Painter { - pub fn ctx(&self) -> &Arc { + pub(crate) fn ctx(&self) -> &Arc { &self.ctx } /// Available fonts - pub fn fonts(&self) -> &Fonts { + pub(crate) fn fonts(&self) -> &Fonts { self.ctx.fonts() } @@ -60,14 +68,17 @@ impl Painter { self.clip_rect = clip_rect; } + /// Useful for pixel-perfect rendering pub fn round_to_pixel(&self, point: f32) -> f32 { self.ctx().round_to_pixel(point) } + /// Useful for pixel-perfect rendering pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 { self.ctx().round_vec_to_pixels(vec) } + /// Useful for pixel-perfect rendering pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 { self.ctx().round_pos_to_pixels(pos) } @@ -110,51 +121,52 @@ impl Painter { outline: Some(LineStyle::new(1.0, color)), rect, }); - let align = (Align::Min, Align::Min); + let anchor = (Align::Min, Align::Min); let text_style = TextStyle::Monospace; - self.floating_text(rect.min, text.into(), text_style, align, color); + self.text(rect.min, anchor, text.into(), text_style, color); } pub fn error(&self, pos: Pos2, text: impl Into) { let text = text.into(); - let align = (Align::Min, Align::Min); + let anchor = (Align::Min, Align::Min); let text_style = TextStyle::Monospace; let font = &self.fonts()[text_style]; let galley = font.layout_multiline(text, f32::INFINITY); - let rect = align_rect(Rect::from_min_size(pos, galley.size), align); + let rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor); self.add(PaintCmd::Rect { corner_radius: 0.0, fill: Some(color::gray(0, 240)), outline: Some(LineStyle::new(1.0, color::RED)), rect: rect.expand(2.0), }); - self.add_galley(rect.min, galley, text_style, color::RED); + self.galley(rect.min, galley, text_style, color::RED); } } /// ## Text impl Painter { - /// Show some text anywhere in the ui. - /// To center the text at the given position, use `align: (Center, Center)`. - /// If you want to draw text floating on top of everything, - /// consider using `Context.floating_text` instead. - pub fn floating_text( + /// Lay out and paint some text. + /// + /// To center the text at the given position, use `anchor: (Center, Center)`. + /// + /// Returns where the text ended up. + pub fn text( &self, pos: Pos2, + anchor: (Align, Align), text: impl Into, text_style: TextStyle, - align: (Align, Align), text_color: Color, ) -> Rect { let font = &self.fonts()[text_style]; let galley = font.layout_multiline(text.into(), f32::INFINITY); - let rect = align_rect(Rect::from_min_size(pos, galley.size), align); - self.add_galley(rect.min, galley, text_style, text_color); + let rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor); + self.galley(rect.min, galley, text_style, text_color); rect } - /// Already layed out text. - pub fn add_galley(&self, pos: Pos2, galley: font::Galley, text_style: TextStyle, color: Color) { + /// Paint text that has already been layed out in a `Galley`. + pub fn galley(&self, pos: Pos2, galley: font::Galley, text_style: TextStyle, color: Color) { self.add(PaintCmd::Text { pos, galley, diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 18fbb3b4..40533bad 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -106,6 +106,14 @@ impl Ui { &self.painter } + /// Create a painter for a sub-region of this Ui. + /// + /// The clip-rect of the returned `Painter` will be the intersection + /// of the given rectangle and the `clip_rect()` of this `Ui`. + pub fn painter_at(&self, rect: Rect) -> Painter { + self.painter().sub_region(rect) + } + /// Use this to paint stuff within this `Ui`. pub fn layer(&self) -> Layer { self.painter().layer() @@ -419,6 +427,12 @@ impl Ui { self.child_count += 1; child_rect } + + /// Ask to allocate a certain amount of space and return a Painter for that region. + pub fn allocate_canvas(&mut self, size: Vec2) -> Painter { + let rect = self.allocate_space(size); + self.painter_at(rect) + } } /// # Adding widgets diff --git a/egui/src/widgets.rs b/egui/src/widgets.rs index b673bdff..743e5589 100644 --- a/egui/src/widgets.rs +++ b/egui/src/widgets.rs @@ -104,7 +104,7 @@ impl Label { pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: font::Galley) { let text_color = self.text_color.unwrap_or_else(|| ui.style().text_color); ui.painter() - .add_galley(pos, galley, self.text_style, text_color); + .galley(pos, galley, self.text_style, text_color); } } @@ -202,7 +202,7 @@ impl Widget for Hyperlink { } ui.painter() - .add_galley(interact.rect.min, galley, text_style, color); + .galley(interact.rect.min, galley, text_style, color); interact } @@ -291,7 +291,7 @@ impl Widget for Button { let stroke_color = ui.style().interact(&interact).stroke_color; let text_color = text_color.unwrap_or(stroke_color); ui.painter() - .add_galley(text_cursor, galley, text_style, text_color); + .galley(text_cursor, galley, text_style, text_color); interact } } @@ -368,7 +368,7 @@ impl<'a> Widget for Checkbox<'a> { let text_color = text_color.unwrap_or(stroke_color); ui.painter() - .add_galley(text_cursor, galley, text_style, text_color); + .galley(text_cursor, galley, text_style, text_color); interact } } @@ -441,7 +441,7 @@ impl Widget for RadioButton { } let text_color = text_color.unwrap_or(stroke_color); - painter.add_galley(text_cursor, galley, text_style, text_color); + painter.galley(text_cursor, galley, text_style, text_color); interact } } diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs index 84fb07da..fd7a2647 100644 --- a/egui/src/widgets/slider.rs +++ b/egui/src/widgets/slider.rs @@ -116,7 +116,7 @@ impl<'a> Widget for Slider<'a> { if text_on_top { let galley = font.layout_single_line(full_text); let pos = ui.allocate_space(galley.size).min; - ui.painter().add_galley(pos, galley, text_style, text_color); + ui.painter().galley(pos, galley, text_style, text_color); slider_sans_text.ui(ui) } else { ui.columns(2, |columns| { diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index 54cf542f..8e0524b5 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -166,7 +166,7 @@ impl<'t> Widget for TextEdit<'t> { } let text_color = text_color.unwrap_or_else(|| ui.style().text_color); - painter.add_galley(interact.rect.min, galley, text_style, text_color); + painter.galley(interact.rect.min, galley, text_style, text_color); ui.memory().text_edit.insert(id, state); interact } diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs index 78c9d35d..a0129b31 100644 --- a/example_glium/src/main.rs +++ b/example_glium/src/main.rs @@ -1,6 +1,9 @@ +#![deny(warnings)] +#![warn(clippy::all)] + use egui_glium::{storage::FileStorage, RunMode}; -/// We dervive Deserialize/Serialize so we can persist app state on shutdown. +/// We derive Deserialize/Serialize so we can persist app state on shutdown. #[derive(Default, serde::Deserialize, serde::Serialize)] struct MyApp { counter: u64,