Add UI to modify `FontTweak` live (#5125)

This will make it easier to get nice sizing and vertical alignments of
fonts
This commit is contained in:
Emil Ernerfeldt 2024-09-18 13:43:33 +02:00 committed by GitHub
parent e31b44f1a5
commit f4ed394a85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 126 additions and 25 deletions

View File

@ -4,9 +4,15 @@ use std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration
use containers::area::AreaState; use containers::area::AreaState;
use epaint::{ use epaint::{
emath, emath::TSTransform, mutex::RwLock, pos2, stats::PaintStats, tessellator, text::Fonts, emath::{self, TSTransform},
util::OrderedFloat, vec2, ClippedPrimitive, ClippedShape, Color32, ImageData, ImageDelta, Pos2, mutex::RwLock,
Rect, TessellationOptions, TextureAtlas, TextureId, Vec2, pos2,
stats::PaintStats,
tessellator,
text::Fonts,
util::OrderedFloat,
vec2, ClippedPrimitive, ClippedShape, Color32, ImageData, ImageDelta, Pos2, Rect,
TessellationOptions, TextureAtlas, TextureId, Vec2,
}; };
use crate::{ use crate::{
@ -2817,6 +2823,10 @@ impl Context {
let prev_options = self.options(|o| o.clone()); let prev_options = self.options(|o| o.clone());
let mut options = prev_options.clone(); let mut options = prev_options.clone();
ui.collapsing("🔠 Font tweak", |ui| {
self.fonts_tweak_ui(ui);
});
options.ui(ui); options.ui(ui);
if options != prev_options { if options != prev_options {
@ -2824,6 +2834,23 @@ impl Context {
} }
} }
fn fonts_tweak_ui(&self, ui: &mut Ui) {
let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
let mut changed = false;
for (name, data) in &mut font_definitions.font_data {
ui.collapsing(name, |ui| {
if data.tweak.ui(ui).changed() {
changed = true;
}
});
}
if changed {
self.set_fonts(font_definitions);
}
}
/// Show the state of egui, including its input and output. /// Show the state of egui, including its input and output.
pub fn inspection_ui(&self, ui: &mut Ui) { pub fn inspection_ui(&self, ui: &mut Ui) {
use crate::containers::CollapsingHeader; use crate::containers::CollapsingHeader;

View File

@ -4,7 +4,7 @@
use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc}; use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc};
use epaint::{Rounding, Shadow, Stroke}; use epaint::{text::FontTweak, Rounding, Shadow, Stroke};
use crate::{ use crate::{
ecolor::Color32, ecolor::Color32,
@ -2498,3 +2498,48 @@ impl Widget for &mut crate::Frame {
.response .response
} }
} }
impl Widget for &mut FontTweak {
fn ui(self, ui: &mut Ui) -> Response {
let original: FontTweak = *self;
let mut response = Grid::new("font_tweak")
.num_columns(2)
.show(ui, |ui| {
let FontTweak {
scale,
y_offset_factor,
y_offset,
baseline_offset_factor,
} = self;
ui.label("Scale");
let speed = *scale * 0.01;
ui.add(DragValue::new(scale).range(0.01..=10.0).speed(speed));
ui.end_row();
ui.label("y_offset_factor");
ui.add(DragValue::new(y_offset_factor).speed(-0.0025));
ui.end_row();
ui.label("y_offset");
ui.add(DragValue::new(y_offset).speed(-0.02));
ui.end_row();
ui.label("baseline_offset_factor");
ui.add(DragValue::new(baseline_offset_factor).speed(-0.0025));
ui.end_row();
if ui.button("Reset").clicked() {
*self = Default::default();
}
})
.response;
if *self != original {
response.mark_changed();
}
response
}
}

View File

@ -1404,7 +1404,7 @@ impl Ui {
/// ///
/// If this is called multiple times per frame for the same [`crate::ScrollArea`], the deltas will be summed. /// If this is called multiple times per frame for the same [`crate::ScrollArea`], the deltas will be summed.
/// ///
/// /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_rect`], [`Ui::scroll_to_cursor`] /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_rect`], [`Ui::scroll_to_cursor`]
/// ///
/// ``` /// ```
/// # use egui::{Align, Vec2}; /// # use egui::{Align, Vec2};

View File

@ -1,9 +1,16 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
struct GlyphInfo {
name: String,
// What fonts it is available in
fonts: Vec<String>,
}
pub struct FontBook { pub struct FontBook {
filter: String, filter: String,
font_id: egui::FontId, font_id: egui::FontId,
named_chars: BTreeMap<egui::FontFamily, BTreeMap<char, String>>, available_glyphs: BTreeMap<egui::FontFamily, BTreeMap<char, GlyphInfo>>,
} }
impl Default for FontBook { impl Default for FontBook {
@ -11,7 +18,7 @@ impl Default for FontBook {
Self { Self {
filter: Default::default(), filter: Default::default(),
font_id: egui::FontId::proportional(18.0), font_id: egui::FontId::proportional(18.0),
named_chars: Default::default(), available_glyphs: Default::default(),
} }
} }
} }
@ -37,7 +44,7 @@ impl crate::View for FontBook {
ui.label(format!( ui.label(format!(
"The selected font supports {} characters.", "The selected font supports {} characters.",
self.named_chars self.available_glyphs
.get(&self.font_id.family) .get(&self.font_id.family)
.map(|map| map.len()) .map(|map| map.len())
.unwrap_or_default() .unwrap_or_default()
@ -67,8 +74,8 @@ impl crate::View for FontBook {
}); });
let filter = &self.filter; let filter = &self.filter;
let named_chars = self let available_glyphs = self
.named_chars .available_glyphs
.entry(self.font_id.family.clone()) .entry(self.font_id.family.clone())
.or_insert_with(|| available_characters(ui, self.font_id.family.clone())); .or_insert_with(|| available_characters(ui, self.font_id.family.clone()));
@ -78,8 +85,11 @@ impl crate::View for FontBook {
ui.horizontal_wrapped(|ui| { ui.horizontal_wrapped(|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::splat(2.0); ui.spacing_mut().item_spacing = egui::Vec2::splat(2.0);
for (&chr, name) in named_chars { for (&chr, glyph_info) in available_glyphs {
if filter.is_empty() || name.contains(filter) || *filter == chr.to_string() { if filter.is_empty()
|| glyph_info.name.contains(filter)
|| *filter == chr.to_string()
{
let button = egui::Button::new( let button = egui::Button::new(
egui::RichText::new(chr.to_string()).font(self.font_id.clone()), egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
) )
@ -89,7 +99,10 @@ impl crate::View for FontBook {
ui.label( ui.label(
egui::RichText::new(chr.to_string()).font(self.font_id.clone()), egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
); );
ui.label(format!("{}\nU+{:X}\n\nClick to copy", name, chr as u32)); ui.label(format!(
"{}\nU+{:X}\n\nFound in: {:?}\n\nClick to copy",
glyph_info.name, chr as u32, glyph_info.fonts
));
}; };
if ui.add(button).on_hover_ui(tooltip_ui).clicked() { if ui.add(button).on_hover_ui(tooltip_ui).clicked() {
@ -102,15 +115,23 @@ impl crate::View for FontBook {
} }
} }
fn available_characters(ui: &egui::Ui, family: egui::FontFamily) -> BTreeMap<char, String> { fn available_characters(ui: &egui::Ui, family: egui::FontFamily) -> BTreeMap<char, GlyphInfo> {
ui.fonts(|f| { ui.fonts(|f| {
f.lock() f.lock()
.fonts .fonts
.font(&egui::FontId::new(10.0, family)) // size is arbitrary for getting the characters .font(&egui::FontId::new(10.0, family)) // size is arbitrary for getting the characters
.characters() .characters()
.iter() .iter()
.filter(|chr| !chr.is_whitespace() && !chr.is_ascii_control()) .filter(|(chr, _fonts)| !chr.is_whitespace() && !chr.is_ascii_control())
.map(|&chr| (chr, char_name(chr))) .map(|(chr, fonts)| {
(
*chr,
GlyphInfo {
name: char_name(*chr),
fonts: fonts.clone(),
},
)
})
.collect() .collect()
}) })
} }

View File

@ -1,11 +1,13 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use emath::{vec2, Vec2};
use crate::{ use crate::{
mutex::{Mutex, RwLock}, mutex::{Mutex, RwLock},
text::FontTweak, text::FontTweak,
TextureAtlas, TextureAtlas,
}; };
use emath::{vec2, Vec2};
use std::collections::BTreeSet;
use std::sync::Arc;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -330,7 +332,7 @@ pub struct Font {
fonts: Vec<Arc<FontImpl>>, fonts: Vec<Arc<FontImpl>>,
/// Lazily calculated. /// Lazily calculated.
characters: Option<BTreeSet<char>>, characters: Option<BTreeMap<char, Vec<String>>>,
replacement_glyph: (FontIndex, GlyphInfo), replacement_glyph: (FontIndex, GlyphInfo),
pixels_per_point: f32, pixels_per_point: f32,
@ -398,12 +400,14 @@ impl Font {
self.glyph_info(crate::text::PASSWORD_REPLACEMENT_CHAR); self.glyph_info(crate::text::PASSWORD_REPLACEMENT_CHAR);
} }
/// All supported characters. /// All supported characters, and in which font they are available in.
pub fn characters(&mut self) -> &BTreeSet<char> { pub fn characters(&mut self) -> &BTreeMap<char, Vec<String>> {
self.characters.get_or_insert_with(|| { self.characters.get_or_insert_with(|| {
let mut characters = BTreeSet::new(); let mut characters: BTreeMap<char, Vec<String>> = Default::default();
for font in &self.fonts { for font in &self.fonts {
characters.extend(font.characters()); for chr in font.characters() {
characters.entry(chr).or_default().push(font.name.clone());
}
} }
characters characters
}) })

View File

@ -159,6 +159,8 @@ pub struct FontTweak {
/// Shift font's glyphs downwards by this fraction of the font size (in points). /// Shift font's glyphs downwards by this fraction of the font size (in points).
/// this is only a visual effect and does not affect the text layout. /// this is only a visual effect and does not affect the text layout.
/// ///
/// Affects larger font sizes more.
///
/// A positive value shifts the text downwards. /// A positive value shifts the text downwards.
/// A negative value shifts it upwards. /// A negative value shifts it upwards.
/// ///
@ -168,6 +170,8 @@ pub struct FontTweak {
/// Shift font's glyphs downwards by this amount of logical points. /// Shift font's glyphs downwards by this amount of logical points.
/// this is only a visual effect and does not affect the text layout. /// this is only a visual effect and does not affect the text layout.
/// ///
/// Affects all font sizes equally.
///
/// Example value: `2.0`. /// Example value: `2.0`.
pub y_offset: f32, pub y_offset: f32,

View File

@ -482,7 +482,7 @@ fn replace_last_glyph_with_overflow_character(
/// Horizontally aligned the text on a row. /// Horizontally aligned the text on a row.
/// ///
/// /// Ignores the Y coordinate. /// Ignores the Y coordinate.
fn halign_and_justify_row( fn halign_and_justify_row(
point_scale: PointScale, point_scale: PointScale,
row: &mut Row, row: &mut Row,