mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -04:00
Add font variations API (#7859)
<!-- Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md) before opening a Pull Request! * Keep your PR:s small and focused. * The PR title is what ends up in the changelog, so make it descriptive! * If applicable, add a screenshot or gif. * If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example. * Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to test and add commits to your PR. * Remember to run `cargo fmt` and `cargo clippy`. * Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`. * When you have addressed a PR comment, mark it as resolved. Please be patient! I will review your PR, but my time is limited! --> * Closes N/A * [x] I have followed the instructions in the PR template This was mostly from last month, but I never got around to submitting it. This PR adds font variation coordinates to the `TextFormat` struct, and uses them when rendering text. The coordinates are stored in a `SmallVec`; I've chosen to store up to 2 inline, which makes it take up 24 bytes (the minimum possible for a `SmallVec`). The variation axis tags are stored as the `font_types::Tag` type, which I've chosen to re-export from `epaint::text`. The variation coordinates are resolved to a `skrifa::Location` during font rendering/scaling, and are cached in the same way as all the other scaled metrics. I've renamed the `ScaledMetrics` struct to `StyledMetrics`, since it now also contains the resolved variation coordinates. I haven't benchmarked the performance of text layout with variation coordinates, but the existing text layout performance is unchanged. I've replaced the API for manually overriding a font's weight (https://github.com/emilk/egui/pull/7790) with an API for manually overriding any variation coordinates via `FontTweak`. This should support the same use case as #7790 while being substantially more flexible. I have *not* yet added any higher-level API for mapping style attributes (weight, width, slant, etc) to variation coordinates or to different font faces within a single family. That's a pretty huge can of worms, and it'd involve rethinking the split between `FontId` and `TextFormat` (and whether `FontId` is so big that we should provide a way to reuse it). This API is intentionally pretty low-level for now. Likewise, I've intentionally not used variation coordinates when computing a font's row height. I can't think of any fonts that change their vertical metrics depending on variation axes, so this should be fine for now. --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
@@ -3262,7 +3262,7 @@ impl Context {
|
||||
|
||||
for (name, data) in &mut font_definitions.font_data {
|
||||
ui.collapsing(name, |ui| {
|
||||
let mut tweak = data.tweak;
|
||||
let mut tweak = data.tweak.clone();
|
||||
if tweak.ui(ui).changed() {
|
||||
Arc::make_mut(data).tweak = tweak;
|
||||
changed = true;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
//! egui theme (spacing, colors, etc).
|
||||
|
||||
use emath::Align;
|
||||
use epaint::{AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions, text::FontTweak};
|
||||
use epaint::{
|
||||
AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions,
|
||||
mutex::Mutex,
|
||||
text::{FontTweak, Tag},
|
||||
};
|
||||
use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
@@ -2837,7 +2841,7 @@ impl Widget for &mut crate::Frame {
|
||||
|
||||
impl Widget for &mut FontTweak {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
let original: FontTweak = *self;
|
||||
let original: FontTweak = self.clone();
|
||||
|
||||
let mut response = Grid::new("font_tweak")
|
||||
.num_columns(2)
|
||||
@@ -2847,6 +2851,7 @@ impl Widget for &mut FontTweak {
|
||||
y_offset_factor,
|
||||
y_offset,
|
||||
hinting_override,
|
||||
coords,
|
||||
} = self;
|
||||
|
||||
ui.label("Scale");
|
||||
@@ -2874,6 +2879,50 @@ impl Widget for &mut FontTweak {
|
||||
ui.selectable_value(hinting_override, Some(true), "Enable");
|
||||
ui.selectable_value(hinting_override, Some(false), "Disable");
|
||||
});
|
||||
ui.end_row();
|
||||
|
||||
ui.label("coords");
|
||||
ui.end_row();
|
||||
let mut to_remove = None;
|
||||
for (i, (tag, value)) in coords.as_mut().iter_mut().enumerate() {
|
||||
let tag_text = ui.ctx().data_mut(|data| {
|
||||
let tag = *tag;
|
||||
Arc::clone(data.get_temp_mut_or_insert_with(ui.id().with(i), move || {
|
||||
Arc::new(Mutex::new(tag.to_string()))
|
||||
}))
|
||||
});
|
||||
|
||||
let tag_text = &mut *tag_text.lock();
|
||||
let response = ui.text_edit_singleline(tag_text);
|
||||
if response.changed()
|
||||
&& let Ok(new_tag) = Tag::new_checked(tag_text.as_bytes())
|
||||
{
|
||||
*tag = new_tag;
|
||||
}
|
||||
// Reset stale text when not actively editing
|
||||
// (e.g. after an item was removed and indices shifted)
|
||||
if !response.has_focus()
|
||||
&& Tag::new_checked(tag_text.as_bytes()).ok() != Some(*tag)
|
||||
{
|
||||
*tag_text = tag.to_string();
|
||||
}
|
||||
|
||||
ui.add(DragValue::new(value));
|
||||
if ui.small_button("🗑").clicked() {
|
||||
to_remove = Some(i);
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
if let Some(i) = to_remove {
|
||||
coords.remove(i);
|
||||
}
|
||||
if ui.button("Add coord").clicked() {
|
||||
coords.push(b"wght", 0.0);
|
||||
}
|
||||
if ui.button("Clear coords").clicked() {
|
||||
coords.clear();
|
||||
}
|
||||
ui.end_row();
|
||||
|
||||
if ui.button("Reset").clicked() {
|
||||
*self = Default::default();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::text::TextFormat;
|
||||
use epaint::text::{IntoTag, TextFormat, VariationCoords};
|
||||
use std::fmt::Formatter;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
@@ -34,6 +34,7 @@ pub struct RichText {
|
||||
background_color: Color32,
|
||||
expand_bg: f32,
|
||||
text_color: Option<Color32>,
|
||||
coords: VariationCoords,
|
||||
code: bool,
|
||||
strong: bool,
|
||||
weak: bool,
|
||||
@@ -55,6 +56,7 @@ impl Default for RichText {
|
||||
background_color: Default::default(),
|
||||
expand_bg: 1.0,
|
||||
text_color: Default::default(),
|
||||
coords: Default::default(),
|
||||
code: Default::default(),
|
||||
strong: Default::default(),
|
||||
weak: Default::default(),
|
||||
@@ -196,6 +198,23 @@ impl RichText {
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a variation coordinate.
|
||||
#[inline]
|
||||
pub fn variation(mut self, tag: impl IntoTag, coord: f32) -> Self {
|
||||
self.coords.push(tag, coord);
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the variation coordinates completely.
|
||||
#[inline]
|
||||
pub fn variations<T: IntoTag>(
|
||||
mut self,
|
||||
variations: impl IntoIterator<Item = (T, f32)>,
|
||||
) -> Self {
|
||||
self.coords = VariationCoords::new(variations);
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the [`TextStyle`].
|
||||
#[inline]
|
||||
pub fn text_style(mut self, text_style: TextStyle) -> Self {
|
||||
@@ -391,6 +410,7 @@ impl RichText {
|
||||
background_color,
|
||||
expand_bg,
|
||||
text_color: _, // already used by `get_text_color`
|
||||
coords,
|
||||
code,
|
||||
strong: _, // already used by `get_text_color`
|
||||
weak: _, // already used by `get_text_color`
|
||||
@@ -449,6 +469,7 @@ impl RichText {
|
||||
line_height,
|
||||
color: text_color,
|
||||
background: background_color,
|
||||
coords,
|
||||
italics,
|
||||
underline,
|
||||
strikethrough,
|
||||
|
||||
Reference in New Issue
Block a user