Add anchored text rotation method, and clarify related docs (#7130)

Add a helper method to perform rotation about a specified anchor.

* Closes #7051
This commit is contained in:
Patrick Marks 2025-06-16 01:42:01 +02:00 committed by GitHub
parent f33ff2c83d
commit df2c16ef0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 116 additions and 5 deletions

View File

@ -1,8 +1,8 @@
use super::{Demo, View};
use egui::{
vec2, Align, Checkbox, CollapsingHeader, Color32, Context, FontId, Resize, RichText, Sense,
Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window,
vec2, Align, Align2, Checkbox, CollapsingHeader, Color32, ComboBox, Context, FontId, Resize,
RichText, Sense, Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window,
};
/// Showcase some ui code
@ -16,6 +16,7 @@ pub struct MiscDemoWindow {
custom_collapsing_header: CustomCollapsingHeader,
tree: Tree,
box_painting: BoxPainting,
text_rotation: TextRotation,
dummy_bool: bool,
dummy_usize: usize,
@ -32,6 +33,7 @@ impl Default for MiscDemoWindow {
custom_collapsing_header: Default::default(),
tree: Tree::demo(),
box_painting: Default::default(),
text_rotation: Default::default(),
dummy_bool: false,
dummy_usize: 0,
@ -79,6 +81,10 @@ impl View for MiscDemoWindow {
});
});
CollapsingHeader::new("Text rotation")
.default_open(false)
.show(ui, |ui| self.text_rotation.ui(ui));
CollapsingHeader::new("Colors")
.default_open(false)
.show(ui, |ui| {
@ -729,3 +735,95 @@ fn text_layout_demo(ui: &mut Ui) {
ui.label(job);
}
// ----------------------------------------------------------------------------
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))]
struct TextRotation {
size: Vec2,
angle: f32,
align: egui::Align2,
}
impl Default for TextRotation {
fn default() -> Self {
Self {
size: vec2(200.0, 200.0),
angle: 0.0,
align: egui::Align2::LEFT_TOP,
}
}
}
impl TextRotation {
pub fn ui(&mut self, ui: &mut Ui) {
ui.add(Slider::new(&mut self.angle, 0.0..=2.0 * std::f32::consts::PI).text("angle"));
let default_color = if ui.visuals().dark_mode {
Color32::LIGHT_GRAY
} else {
Color32::DARK_GRAY
};
let aligns = [
(Align2::LEFT_TOP, "LEFT_TOP"),
(Align2::LEFT_CENTER, "LEFT_CENTER"),
(Align2::LEFT_BOTTOM, "LEFT_BOTTOM"),
(Align2::CENTER_TOP, "CENTER_TOP"),
(Align2::CENTER_CENTER, "CENTER_CENTER"),
(Align2::CENTER_BOTTOM, "CENTER_BOTTOM"),
(Align2::RIGHT_TOP, "RIGHT_TOP"),
(Align2::RIGHT_CENTER, "RIGHT_CENTER"),
(Align2::RIGHT_BOTTOM, "RIGHT_BOTTOM"),
];
ComboBox::new("anchor", "Anchor")
.selected_text(aligns.iter().find(|(a, _)| *a == self.align).unwrap().1)
.show_ui(ui, |ui| {
for (align2, name) in &aligns {
ui.selectable_value(&mut self.align, *align2, *name);
}
});
ui.horizontal_wrapped(|ui| {
let (response, painter) = ui.allocate_painter(self.size, Sense::empty());
let rect = response.rect;
let start_pos = self.size / 2.0;
let s = ui.ctx().fonts(|f| {
let mut t = egui::Shape::text(
f,
rect.min + start_pos,
egui::Align2::LEFT_TOP,
"sample_text",
egui::FontId::new(12.0, egui::FontFamily::Proportional),
default_color,
);
if let egui::epaint::Shape::Text(ts) = &mut t {
let new = ts.clone().with_angle_and_anchor(self.angle, self.align);
*ts = new;
};
t
});
if let egui::epaint::Shape::Text(ts) = &s {
let align_pt =
rect.min + start_pos + self.align.pos_in_rect(&ts.galley.rect).to_vec2();
painter.circle(align_pt, 2.0, Color32::RED, (0.0, Color32::RED));
};
painter.rect(
rect,
0.0,
default_color.gamma_multiply(0.3),
(0.0, Color32::BLACK),
egui::StrokeKind::Middle,
);
painter.add(s);
});
}
}

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:880344367ed65f83898ceca4843b1b6259d1690242ced0d29ac8dc48100a8faa
size 62956
oid sha256:116a53258be27d9c7c56538e5f83202ea731f19887fabadc0449d24fde4d80d9
size 64494

View File

@ -1,5 +1,7 @@
use std::sync::Arc;
use emath::{Align2, Rot2};
use crate::*;
/// How to paint some text on screen.
@ -78,7 +80,7 @@ impl TextShape {
self
}
/// Rotate text by this many radians clockwise.
/// Set text rotation to `angle` radians clockwise.
/// The pivot is `pos` (the upper left corner of the text).
#[inline]
pub fn with_angle(mut self, angle: f32) -> Self {
@ -86,6 +88,17 @@ impl TextShape {
self
}
/// Set the text rotation to the `angle` radians clockwise.
/// The pivot is determined by the given `anchor` point on the text bounding box.
#[inline]
pub fn with_angle_and_anchor(mut self, angle: f32, anchor: Align2) -> Self {
self.angle = angle;
let a0 = anchor.pos_in_rect(&self.galley.rect).to_vec2();
let a1 = Rot2::from_angle(angle) * a0;
self.pos += a0 - a1;
self
}
/// Render text with this opacity in gamma space
#[inline]
pub fn with_opacity_factor(mut self, opacity_factor: f32) -> Self {