mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 12:50:04 -04:00
Implement rotating text
Closes https://github.com/emilk/egui/issues/428
This commit is contained in:
@@ -93,7 +93,7 @@ pub use {
|
||||
color::{Color32, Rgba},
|
||||
mesh::{Mesh, Mesh16, Vertex},
|
||||
shadow::Shadow,
|
||||
shape::Shape,
|
||||
shape::{Shape, TextShape},
|
||||
stats::PaintStats,
|
||||
stroke::Stroke,
|
||||
tessellator::{TessellationOptions, Tessellator},
|
||||
|
||||
@@ -40,22 +40,7 @@ pub enum Shape {
|
||||
fill: Color32,
|
||||
stroke: Stroke,
|
||||
},
|
||||
Text {
|
||||
/// Top left corner of the first character..
|
||||
pos: Pos2,
|
||||
|
||||
/// The layed out text.
|
||||
galley: std::sync::Arc<Galley>,
|
||||
|
||||
/// Add this underline to the whole text.
|
||||
/// You can also set an underline when creating the galley.
|
||||
underline: Stroke,
|
||||
|
||||
/// If set, the text color in the galley will be ignored and replaced
|
||||
/// with the given color.
|
||||
/// This will NOT replace background color nor strikethrough/underline color.
|
||||
override_text_color: Option<Color32>,
|
||||
},
|
||||
Text(TextShape),
|
||||
Mesh(Mesh),
|
||||
}
|
||||
|
||||
@@ -181,15 +166,57 @@ impl Shape {
|
||||
}
|
||||
|
||||
pub fn galley(pos: Pos2, galley: std::sync::Arc<Galley>) -> Self {
|
||||
Self::Text {
|
||||
TextShape::new(pos, galley).into()
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// How to draw some text on screen.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TextShape {
|
||||
/// Top left corner of the first character.
|
||||
pub pos: Pos2,
|
||||
|
||||
/// The layed out text, from [`Fonts::layout_job`].
|
||||
pub galley: std::sync::Arc<Galley>,
|
||||
|
||||
/// Add this underline to the whole text.
|
||||
/// You can also set an underline when creating the galley.
|
||||
pub underline: Stroke,
|
||||
|
||||
/// If set, the text color in the galley will be ignored and replaced
|
||||
/// with the given color.
|
||||
/// This will NOT replace background color nor strikethrough/underline color.
|
||||
pub override_text_color: Option<Color32>,
|
||||
|
||||
/// Rotate text by this many radians clock-wise.
|
||||
/// The pivot is `pos` (the upper left corner of the text).
|
||||
pub angle: f32,
|
||||
}
|
||||
|
||||
impl TextShape {
|
||||
#[inline]
|
||||
pub fn new(pos: Pos2, galley: std::sync::Arc<Galley>) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
galley,
|
||||
override_text_color: None,
|
||||
underline: Stroke::none(),
|
||||
override_text_color: None,
|
||||
angle: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Shape> for TextShape {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Shape {
|
||||
Shape::Text(self)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Creates equally spaced filled circles from a line.
|
||||
fn points_from_line(
|
||||
line: &[Pos2],
|
||||
@@ -294,8 +321,8 @@ impl Shape {
|
||||
Shape::Rect { rect, .. } => {
|
||||
*rect = rect.translate(delta);
|
||||
}
|
||||
Shape::Text { pos, .. } => {
|
||||
*pos += delta;
|
||||
Shape::Text(text_shape) => {
|
||||
text_shape.pos += delta;
|
||||
}
|
||||
Shape::Mesh(mesh) => {
|
||||
mesh.translate(delta);
|
||||
|
||||
@@ -24,17 +24,13 @@ pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) {
|
||||
adjust_color(fill);
|
||||
adjust_color(&mut stroke.color);
|
||||
}
|
||||
Shape::Text {
|
||||
galley,
|
||||
override_text_color,
|
||||
..
|
||||
} => {
|
||||
if let Some(override_text_color) = override_text_color {
|
||||
Shape::Text(text_shape) => {
|
||||
if let Some(override_text_color) = &mut text_shape.override_text_color {
|
||||
adjust_color(override_text_color);
|
||||
}
|
||||
|
||||
if !galley.is_empty() {
|
||||
let galley = std::sync::Arc::make_mut(galley);
|
||||
if !text_shape.galley.is_empty() {
|
||||
let galley = std::sync::Arc::make_mut(&mut text_shape.galley);
|
||||
for row in &mut galley.rows {
|
||||
for vertex in &mut row.visuals.mesh.vertices {
|
||||
adjust_color(&mut vertex.color);
|
||||
|
||||
@@ -198,8 +198,8 @@ impl PaintStats {
|
||||
Shape::Path { points, .. } => {
|
||||
self.shape_path += AllocInfo::from_slice(points);
|
||||
}
|
||||
Shape::Text { galley, .. } => {
|
||||
self.shape_text += AllocInfo::from_galley(galley);
|
||||
Shape::Text(text_shape) => {
|
||||
self.shape_text += AllocInfo::from_galley(&text_shape.galley);
|
||||
}
|
||||
Shape::Mesh(mesh) => {
|
||||
self.shape_mesh += AllocInfo::from_mesh(mesh);
|
||||
|
||||
@@ -617,16 +617,12 @@ impl Tessellator {
|
||||
};
|
||||
self.tessellate_rect(&rect, out);
|
||||
}
|
||||
Shape::Text {
|
||||
pos,
|
||||
galley,
|
||||
underline,
|
||||
override_text_color,
|
||||
} => {
|
||||
Shape::Text(text_shape) => {
|
||||
if options.debug_paint_text_rects {
|
||||
self.tessellate_rect(
|
||||
&PaintRect {
|
||||
rect: Rect::from_min_size(pos, galley.size).expand(0.5),
|
||||
rect: Rect::from_min_size(text_shape.pos, text_shape.galley.size)
|
||||
.expand(0.5),
|
||||
corner_radius: 2.0,
|
||||
fill: Default::default(),
|
||||
stroke: (0.5, Color32::GREEN).into(),
|
||||
@@ -634,7 +630,7 @@ impl Tessellator {
|
||||
out,
|
||||
);
|
||||
}
|
||||
self.tessellate_text(tex_size, pos, &galley, underline, override_text_color, out);
|
||||
self.tessellate_text(tex_size, text_shape, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -669,15 +665,15 @@ impl Tessellator {
|
||||
path.stroke_closed(stroke, self.options, out);
|
||||
}
|
||||
|
||||
pub fn tessellate_text(
|
||||
&mut self,
|
||||
tex_size: [usize; 2],
|
||||
galley_pos: Pos2,
|
||||
galley: &super::Galley,
|
||||
underline: Stroke,
|
||||
override_text_color: Option<Color32>,
|
||||
out: &mut Mesh,
|
||||
) {
|
||||
pub fn tessellate_text(&mut self, tex_size: [usize; 2], text_shape: TextShape, out: &mut Mesh) {
|
||||
let TextShape {
|
||||
pos: galley_pos,
|
||||
galley,
|
||||
underline,
|
||||
override_text_color,
|
||||
angle,
|
||||
} = text_shape;
|
||||
|
||||
if galley.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -694,12 +690,18 @@ impl Tessellator {
|
||||
|
||||
let uv_normalizer = vec2(1.0 / tex_size[0] as f32, 1.0 / tex_size[1] as f32);
|
||||
|
||||
let rotator = Rot2::from_angle(angle);
|
||||
|
||||
for row in &galley.rows {
|
||||
if row.visuals.mesh.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let row_rect = row.visuals.mesh_bounds.translate(galley_pos.to_vec2());
|
||||
let mut row_rect = row.visuals.mesh_bounds;
|
||||
if angle != 0.0 {
|
||||
row_rect = row_rect.rotate_bb(rotator);
|
||||
}
|
||||
row_rect = row_rect.translate(galley_pos.to_vec2());
|
||||
|
||||
if self.options.coarse_tessellation_culling && !self.clip_rect.intersects(row_rect) {
|
||||
// culling individual lines of text is important, since a single `Shape::Text`
|
||||
@@ -724,7 +726,7 @@ impl Tessellator {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, vertex)| {
|
||||
let mut color = vertex.color;
|
||||
let Vertex { pos, uv, mut color } = *vertex;
|
||||
|
||||
if let Some(override_text_color) = override_text_color {
|
||||
if row.visuals.glyph_vertex_range.contains(&i) {
|
||||
@@ -732,9 +734,15 @@ impl Tessellator {
|
||||
}
|
||||
}
|
||||
|
||||
let offset = if angle == 0.0 {
|
||||
pos.to_vec2()
|
||||
} else {
|
||||
rotator * pos.to_vec2()
|
||||
};
|
||||
|
||||
Vertex {
|
||||
pos: galley_pos + vertex.pos.to_vec2(),
|
||||
uv: (vertex.uv.to_vec2() * uv_normalizer).to_pos2(),
|
||||
pos: galley_pos + offset,
|
||||
uv: (uv.to_vec2() * uv_normalizer).to_pos2(),
|
||||
color,
|
||||
}
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user