[egui] Clean up Painter interface
This commit is contained in:
parent
efedb09b9f
commit
d4cb2fa62e
11 changed files with 100 additions and 31 deletions
|
@ -235,7 +235,7 @@ impl CollapsingHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
let painter = ui.painter();
|
let painter = ui.painter();
|
||||||
painter.add_galley(
|
painter.galley(
|
||||||
text_pos,
|
text_pos,
|
||||||
galley,
|
galley,
|
||||||
label.text_style,
|
label.text_style,
|
||||||
|
|
|
@ -125,14 +125,17 @@ impl Context {
|
||||||
(point * self.input.pixels_per_point).round() / self.input.pixels_per_point
|
(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 {
|
pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {
|
||||||
pos2(self.round_to_pixel(pos.x), self.round_to_pixel(pos.y))
|
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 {
|
pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {
|
||||||
vec2(self.round_to_pixel(vec.x), self.round_to_pixel(vec.y))
|
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 {
|
pub fn round_rect_to_pixels(&self, rect: Rect) -> Rect {
|
||||||
Rect {
|
Rect {
|
||||||
min: self.round_pos_to_pixels(rect.min),
|
min: self.round_pos_to_pixels(rect.min),
|
||||||
|
|
|
@ -39,13 +39,13 @@ impl Default for Align {
|
||||||
|
|
||||||
/// Used e.g. to anchor a piece of text to a part of the rectangle.
|
/// 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
|
/// Give a position within the rect, specified by the aligns
|
||||||
pub(crate) fn align_rect(rect: Rect, align: (Align, Align)) -> Rect {
|
pub(crate) fn anchor_rect(rect: Rect, anchor: (Align, Align)) -> Rect {
|
||||||
let x = match align.0 {
|
let x = match anchor.0 {
|
||||||
Align::Min => rect.left(),
|
Align::Min => rect.left(),
|
||||||
Align::Center => rect.left() - 0.5 * rect.width(),
|
Align::Center => rect.left() - 0.5 * rect.width(),
|
||||||
Align::Max => rect.left() - rect.width(),
|
Align::Max => rect.left() - rect.width(),
|
||||||
};
|
};
|
||||||
let y = match align.1 {
|
let y = match anchor.1 {
|
||||||
Align::Min => rect.top(),
|
Align::Min => rect.top(),
|
||||||
Align::Center => rect.top() - 0.5 * rect.height(),
|
Align::Center => rect.top() - 0.5 * rect.height(),
|
||||||
Align::Max => rect.top() - rect.height(),
|
Align::Max => rect.top() - rect.height(),
|
||||||
|
|
|
@ -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 {
|
impl Vec2 {
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
Self { x: 0.0, y: 0.0 }
|
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 {
|
impl Pos2 {
|
||||||
|
pub fn new(x: f32, y: f32) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_vec2(self) -> Vec2 {
|
pub fn to_vec2(self) -> Vec2 {
|
||||||
Vec2 {
|
Vec2 {
|
||||||
x: self.x,
|
x: self.x,
|
||||||
|
@ -492,6 +508,9 @@ impl Rect {
|
||||||
pub fn height(&self) -> f32 {
|
pub fn height(&self) -> f32 {
|
||||||
self.max.y - self.min.y
|
self.max.y - self.min.y
|
||||||
}
|
}
|
||||||
|
pub fn area(&self) -> f32 {
|
||||||
|
self.width() * self.height()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn range_x(&self) -> RangeInclusive<f32> {
|
pub fn range_x(&self) -> RangeInclusive<f32> {
|
||||||
self.min.x..=self.max.x
|
self.min.x..=self.max.x
|
||||||
|
|
|
@ -10,9 +10,9 @@ pub enum PaintCmd {
|
||||||
Noop,
|
Noop,
|
||||||
Circle {
|
Circle {
|
||||||
center: Pos2,
|
center: Pos2,
|
||||||
|
radius: f32,
|
||||||
fill: Option<Color>,
|
fill: Option<Color>,
|
||||||
outline: Option<LineStyle>,
|
outline: Option<LineStyle>,
|
||||||
radius: f32,
|
|
||||||
},
|
},
|
||||||
LineSegment {
|
LineSegment {
|
||||||
points: [Pos2; 2],
|
points: [Pos2; 2],
|
||||||
|
@ -48,6 +48,24 @@ impl PaintCmd {
|
||||||
style: LineStyle::new(width, color),
|
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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
align_rect, color,
|
anchor_rect, color,
|
||||||
layers::PaintCmdIdx,
|
layers::PaintCmdIdx,
|
||||||
math::{Pos2, Rect, Vec2},
|
math::{Pos2, Rect, Vec2},
|
||||||
paint::{font, Fonts, LineStyle, PaintCmd, TextStyle},
|
paint::{font, Fonts, LineStyle, PaintCmd, TextStyle},
|
||||||
|
@ -30,16 +30,24 @@ impl Painter {
|
||||||
clip_rect,
|
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
|
/// ## Accessors etc
|
||||||
impl Painter {
|
impl Painter {
|
||||||
pub fn ctx(&self) -> &Arc<Context> {
|
pub(crate) fn ctx(&self) -> &Arc<Context> {
|
||||||
&self.ctx
|
&self.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Available fonts
|
/// Available fonts
|
||||||
pub fn fonts(&self) -> &Fonts {
|
pub(crate) fn fonts(&self) -> &Fonts {
|
||||||
self.ctx.fonts()
|
self.ctx.fonts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,14 +68,17 @@ impl Painter {
|
||||||
self.clip_rect = clip_rect;
|
self.clip_rect = clip_rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Useful for pixel-perfect rendering
|
||||||
pub fn round_to_pixel(&self, point: f32) -> f32 {
|
pub fn round_to_pixel(&self, point: f32) -> f32 {
|
||||||
self.ctx().round_to_pixel(point)
|
self.ctx().round_to_pixel(point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Useful for pixel-perfect rendering
|
||||||
pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {
|
pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {
|
||||||
self.ctx().round_vec_to_pixels(vec)
|
self.ctx().round_vec_to_pixels(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Useful for pixel-perfect rendering
|
||||||
pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {
|
pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {
|
||||||
self.ctx().round_pos_to_pixels(pos)
|
self.ctx().round_pos_to_pixels(pos)
|
||||||
}
|
}
|
||||||
|
@ -110,51 +121,52 @@ impl Painter {
|
||||||
outline: Some(LineStyle::new(1.0, color)),
|
outline: Some(LineStyle::new(1.0, color)),
|
||||||
rect,
|
rect,
|
||||||
});
|
});
|
||||||
let align = (Align::Min, Align::Min);
|
let anchor = (Align::Min, Align::Min);
|
||||||
let text_style = TextStyle::Monospace;
|
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<String>) {
|
pub fn error(&self, pos: Pos2, text: impl Into<String>) {
|
||||||
let text = text.into();
|
let text = text.into();
|
||||||
let align = (Align::Min, Align::Min);
|
let anchor = (Align::Min, Align::Min);
|
||||||
let text_style = TextStyle::Monospace;
|
let text_style = TextStyle::Monospace;
|
||||||
let font = &self.fonts()[text_style];
|
let font = &self.fonts()[text_style];
|
||||||
let galley = font.layout_multiline(text, f32::INFINITY);
|
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 {
|
self.add(PaintCmd::Rect {
|
||||||
corner_radius: 0.0,
|
corner_radius: 0.0,
|
||||||
fill: Some(color::gray(0, 240)),
|
fill: Some(color::gray(0, 240)),
|
||||||
outline: Some(LineStyle::new(1.0, color::RED)),
|
outline: Some(LineStyle::new(1.0, color::RED)),
|
||||||
rect: rect.expand(2.0),
|
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
|
/// ## Text
|
||||||
impl Painter {
|
impl Painter {
|
||||||
/// Show some text anywhere in the ui.
|
/// Lay out and paint some text.
|
||||||
/// To center the text at the given position, use `align: (Center, Center)`.
|
///
|
||||||
/// If you want to draw text floating on top of everything,
|
/// To center the text at the given position, use `anchor: (Center, Center)`.
|
||||||
/// consider using `Context.floating_text` instead.
|
///
|
||||||
pub fn floating_text(
|
/// Returns where the text ended up.
|
||||||
|
pub fn text(
|
||||||
&self,
|
&self,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
|
anchor: (Align, Align),
|
||||||
text: impl Into<String>,
|
text: impl Into<String>,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
align: (Align, Align),
|
|
||||||
text_color: Color,
|
text_color: Color,
|
||||||
) -> Rect {
|
) -> Rect {
|
||||||
let font = &self.fonts()[text_style];
|
let font = &self.fonts()[text_style];
|
||||||
let galley = font.layout_multiline(text.into(), f32::INFINITY);
|
let galley = font.layout_multiline(text.into(), 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_galley(rect.min, galley, text_style, text_color);
|
self.galley(rect.min, galley, text_style, text_color);
|
||||||
rect
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Already layed out text.
|
/// Paint text that has already been layed out in a `Galley`.
|
||||||
pub fn add_galley(&self, pos: Pos2, galley: font::Galley, text_style: TextStyle, color: Color) {
|
pub fn galley(&self, pos: Pos2, galley: font::Galley, text_style: TextStyle, color: Color) {
|
||||||
self.add(PaintCmd::Text {
|
self.add(PaintCmd::Text {
|
||||||
pos,
|
pos,
|
||||||
galley,
|
galley,
|
||||||
|
|
|
@ -106,6 +106,14 @@ impl Ui {
|
||||||
&self.painter
|
&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`.
|
/// Use this to paint stuff within this `Ui`.
|
||||||
pub fn layer(&self) -> Layer {
|
pub fn layer(&self) -> Layer {
|
||||||
self.painter().layer()
|
self.painter().layer()
|
||||||
|
@ -419,6 +427,12 @@ 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.
|
||||||
|
pub fn allocate_canvas(&mut self, size: Vec2) -> Painter {
|
||||||
|
let rect = self.allocate_space(size);
|
||||||
|
self.painter_at(rect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Adding widgets
|
/// # Adding widgets
|
||||||
|
|
|
@ -104,7 +104,7 @@ impl Label {
|
||||||
pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: font::Galley) {
|
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);
|
let text_color = self.text_color.unwrap_or_else(|| ui.style().text_color);
|
||||||
ui.painter()
|
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()
|
ui.painter()
|
||||||
.add_galley(interact.rect.min, galley, text_style, color);
|
.galley(interact.rect.min, galley, text_style, color);
|
||||||
|
|
||||||
interact
|
interact
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,7 @@ impl Widget for Button {
|
||||||
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);
|
||||||
ui.painter()
|
ui.painter()
|
||||||
.add_galley(text_cursor, galley, text_style, text_color);
|
.galley(text_cursor, galley, text_style, text_color);
|
||||||
interact
|
interact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ impl<'a> Widget for Checkbox<'a> {
|
||||||
|
|
||||||
let text_color = text_color.unwrap_or(stroke_color);
|
let text_color = text_color.unwrap_or(stroke_color);
|
||||||
ui.painter()
|
ui.painter()
|
||||||
.add_galley(text_cursor, galley, text_style, text_color);
|
.galley(text_cursor, galley, text_style, text_color);
|
||||||
interact
|
interact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,7 +441,7 @@ impl Widget for RadioButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_color = text_color.unwrap_or(stroke_color);
|
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
|
interact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ impl<'a> Widget for Slider<'a> {
|
||||||
if text_on_top {
|
if text_on_top {
|
||||||
let galley = font.layout_single_line(full_text);
|
let galley = font.layout_single_line(full_text);
|
||||||
let pos = ui.allocate_space(galley.size).min;
|
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)
|
slider_sans_text.ui(ui)
|
||||||
} else {
|
} else {
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
|
|
|
@ -166,7 +166,7 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_color = text_color.unwrap_or_else(|| ui.style().text_color);
|
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);
|
ui.memory().text_edit.insert(id, state);
|
||||||
interact
|
interact
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
|
||||||
use egui_glium::{storage::FileStorage, RunMode};
|
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)]
|
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||||
struct MyApp {
|
struct MyApp {
|
||||||
counter: u64,
|
counter: u64,
|
||||||
|
|
Loading…
Reference in a new issue