From 29fa63317ea4f950a098be6240efc65c14b8c7a5 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 21 Sep 2022 21:31:08 +0200 Subject: [PATCH] Fix text sizes being too small (#2069) Closes https://github.com/emilk/egui/issues/2068 Before this PR, the default font, Ubuntu-Light, was ~11% smaller than it should have been, and the default monospace font, Hack, was ~14% smaller. This means that setting the font size `12` in egui would yield smaller text than using that font size in any other app. Ooops! The change is that this PR now takes into account the ttf properties `units_per_em` and `height_unscaled`. If your egui application has specified you own font sizes or text styles you will see the text in your application grow larger, unless you go in and compensate by dividing all font sizes by ~1.21 for Ubuntu-Light/Proportional and ~1.16 for Hack/Monospace, and with something else if you are using a custom font! This effects any use of `FontId`, `RichText::size`, etc. This PR changes the default `Style::text_styles` to compensate, so the default egui style should look the same before and after this PR. --- CHANGELOG.md | 1 + crates/egui/src/introspection.rs | 2 +- crates/egui/src/painter.rs | 4 +- crates/egui/src/style.rs | 29 +++------ crates/egui_demo_lib/src/color_test.rs | 10 +-- .../src/demo/demo_app_windows.rs | 4 +- crates/egui_demo_lib/src/demo/font_book.rs | 2 +- .../src/demo/misc_demo_window.rs | 8 +-- .../egui_demo_lib/src/syntax_highlighting.rs | 8 +-- crates/epaint/CHANGELOG.md | 1 + crates/epaint/src/text/fonts.rs | 65 ++++++++++++++----- crates/epaint/src/text/text_layout.rs | 2 +- examples/custom_font_style/src/main.rs | 14 ++-- examples/custom_window_frame/src/main.rs | 2 +- 14 files changed, 87 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cb01f62..12c6ab23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG ## Unreleased +* ⚠️ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)). ## 0.19.0 - 2022-08-20 diff --git a/crates/egui/src/introspection.rs b/crates/egui/src/introspection.rs index c0003695..442fdc9f 100644 --- a/crates/egui/src/introspection.rs +++ b/crates/egui/src/introspection.rs @@ -14,7 +14,7 @@ pub fn font_family_ui(ui: &mut Ui, font_family: &mut FontFamily) { pub fn font_id_ui(ui: &mut Ui, font_id: &mut FontId) { let families = ui.fonts().families(); ui.horizontal(|ui| { - ui.add(Slider::new(&mut font_id.size, 4.0..=40.0).max_decimals(0)); + ui.add(Slider::new(&mut font_id.size, 4.0..=40.0).max_decimals(1)); for alternative in families { let text = alternative.to_string(); ui.radio_value(&mut font_id.family, alternative, text); diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index e9a13054..47ac4458 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -213,7 +213,7 @@ impl Painter { rect.min, Align2::LEFT_TOP, text.to_string(), - FontId::monospace(14.0), + FontId::monospace(12.0), color, ); } @@ -232,7 +232,7 @@ impl Painter { color: Color32, text: impl ToString, ) -> Rect { - let galley = self.layout_no_wrap(text.to_string(), FontId::monospace(14.0), color); + let galley = self.layout_no_wrap(text.to_string(), FontId::monospace(12.0), color); let rect = anchor.anchor_rect(Rect::from_min_size(pos, galley.size())); let frame_rect = rect.expand(2.0); self.add(Shape::rect_filled( diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 6fb5727e..b83c0acb 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -602,25 +602,16 @@ pub struct DebugOptions { /// The default text styles of the default egui theme. pub fn default_text_styles() -> BTreeMap { - let mut text_styles = BTreeMap::new(); - text_styles.insert( - TextStyle::Small, - FontId::new(10.0, FontFamily::Proportional), - ); - text_styles.insert(TextStyle::Body, FontId::new(14.0, FontFamily::Proportional)); - text_styles.insert( - TextStyle::Button, - FontId::new(14.0, FontFamily::Proportional), - ); - text_styles.insert( - TextStyle::Heading, - FontId::new(20.0, FontFamily::Proportional), - ); - text_styles.insert( - TextStyle::Monospace, - FontId::new(14.0, FontFamily::Monospace), - ); - text_styles + use FontFamily::{Monospace, Proportional}; + + [ + (TextStyle::Small, FontId::new(9.0, Proportional)), + (TextStyle::Body, FontId::new(12.5, Proportional)), + (TextStyle::Button, FontId::new(12.5, Proportional)), + (TextStyle::Heading, FontId::new(18.0, Proportional)), + (TextStyle::Monospace, FontId::new(12.0, Monospace)), + ] + .into() } impl Default for Style { diff --git a/crates/egui_demo_lib/src/color_test.rs b/crates/egui_demo_lib/src/color_test.rs index 3817e604..ae3b347a 100644 --- a/crates/egui_demo_lib/src/color_test.rs +++ b/crates/egui_demo_lib/src/color_test.rs @@ -405,21 +405,21 @@ fn paint_fine_lines_and_text(painter: &egui::Painter, mut rect: Rect, color: Col rect.center_top() + vec2(0.0, x), Align2::LEFT_TOP, format!("{:.0}% white", 100.0 * opacity), - FontId::proportional(16.0), + FontId::proportional(14.0), Color32::WHITE.linear_multiply(opacity), ); painter.text( rect.center_top() + vec2(80.0, x), Align2::LEFT_TOP, format!("{:.0}% gray", 100.0 * opacity), - FontId::proportional(16.0), + FontId::proportional(14.0), Color32::GRAY.linear_multiply(opacity), ); painter.text( rect.center_top() + vec2(160.0, x), Align2::LEFT_TOP, format!("{:.0}% black", 100.0 * opacity), - FontId::proportional(16.0), + FontId::proportional(14.0), Color32::BLACK.linear_multiply(opacity), ); x += 20.0; @@ -434,7 +434,7 @@ fn paint_fine_lines_and_text(painter: &egui::Painter, mut rect: Rect, color: Col rect.left_top(), Align2::CENTER_CENTER, width.to_string(), - FontId::monospace(14.0), + FontId::monospace(12.0), color, ); @@ -459,7 +459,7 @@ fn paint_fine_lines_and_text(painter: &egui::Painter, mut rect: Rect, color: Col rect.left_top(), Align2::LEFT_CENTER, "transparent --> opaque", - FontId::monospace(11.0), + FontId::monospace(10.0), color, ); rect.min.y += 12.0; diff --git a/crates/egui_demo_lib/src/demo/demo_app_windows.rs b/crates/egui_demo_lib/src/demo/demo_app_windows.rs index 50f8c394..c5fe3bb6 100644 --- a/crates/egui_demo_lib/src/demo/demo_app_windows.rs +++ b/crates/egui_demo_lib/src/demo/demo_app_windows.rs @@ -194,7 +194,7 @@ impl DemoWindows { ui.add_space(12.0); ui.vertical_centered_justified(|ui| { if ui - .button(egui::RichText::new("Continue to the demo!").size(24.0)) + .button(egui::RichText::new("Continue to the demo!").size(20.0)) .clicked() { close = true; @@ -211,7 +211,7 @@ impl DemoWindows { fn mobile_top_bar(&mut self, ctx: &Context) { egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| { egui::menu::bar(ui, |ui| { - let font_size = 20.0; + let font_size = 16.5; ui.menu_button(egui::RichText::new("⏷ demos").size(font_size), |ui| { ui.set_style(ui.ctx().style()); // ignore the "menu" style set by `menu_button`. diff --git a/crates/egui_demo_lib/src/demo/font_book.rs b/crates/egui_demo_lib/src/demo/font_book.rs index 8d23b294..324a85a7 100644 --- a/crates/egui_demo_lib/src/demo/font_book.rs +++ b/crates/egui_demo_lib/src/demo/font_book.rs @@ -10,7 +10,7 @@ impl Default for FontBook { fn default() -> Self { Self { filter: Default::default(), - font_id: egui::FontId::proportional(20.0), + font_id: egui::FontId::proportional(18.0), named_chars: Default::default(), } } diff --git a/crates/egui_demo_lib/src/demo/misc_demo_window.rs b/crates/egui_demo_lib/src/demo/misc_demo_window.rs index 80d793a8..f4307db9 100644 --- a/crates/egui_demo_lib/src/demo/misc_demo_window.rs +++ b/crates/egui_demo_lib/src/demo/misc_demo_window.rs @@ -544,7 +544,7 @@ fn text_layout_ui( "mixing ", 0.0, TextFormat { - font_id: FontId::proportional(20.0), + font_id: FontId::proportional(17.0), color: default_color, ..Default::default() }, @@ -553,7 +553,7 @@ fn text_layout_ui( "fonts, ", 0.0, TextFormat { - font_id: FontId::monospace(14.0), + font_id: FontId::monospace(12.0), color: default_color, ..Default::default() }, @@ -562,7 +562,7 @@ fn text_layout_ui( "raised text, ", 0.0, TextFormat { - font_id: FontId::proportional(8.0), + font_id: FontId::proportional(7.0), color: default_color, valign: Align::TOP, ..Default::default() @@ -623,7 +623,7 @@ fn text_layout_ui( " mix these!", 0.0, TextFormat { - font_id: FontId::proportional(8.0), + font_id: FontId::proportional(7.0), color: Color32::LIGHT_BLUE, background: Color32::from_rgb(128, 0, 0), underline: Stroke::new(1.0, strong_color), diff --git a/crates/egui_demo_lib/src/syntax_highlighting.rs b/crates/egui_demo_lib/src/syntax_highlighting.rs index 80c0c806..550e5c98 100644 --- a/crates/egui_demo_lib/src/syntax_highlighting.rs +++ b/crates/egui_demo_lib/src/syntax_highlighting.rs @@ -195,7 +195,7 @@ impl CodeTheme { #[cfg(not(feature = "syntect"))] impl CodeTheme { pub fn dark() -> Self { - let font_id = egui::FontId::monospace(12.0); + let font_id = egui::FontId::monospace(10.0); use egui::{Color32, TextFormat}; Self { dark_mode: true, @@ -211,7 +211,7 @@ impl CodeTheme { } pub fn light() -> Self { - let font_id = egui::FontId::monospace(12.0); + let font_id = egui::FontId::monospace(10.0); use egui::{Color32, TextFormat}; Self { dark_mode: false, @@ -318,7 +318,7 @@ impl Highlighter { // Fallback: LayoutJob::simple( code.into(), - egui::FontId::monospace(14.0), + egui::FontId::monospace(12.0), if theme.dark_mode { egui::Color32::LIGHT_GRAY } else { @@ -364,7 +364,7 @@ impl Highlighter { leading_space: 0.0, byte_range: as_byte_range(text, range), format: TextFormat { - font_id: egui::FontId::monospace(14.0), + font_id: egui::FontId::monospace(12.0), color: text_color, italics, underline, diff --git a/crates/epaint/CHANGELOG.md b/crates/epaint/CHANGELOG.md index 56f57367..86235dce 100644 --- a/crates/epaint/CHANGELOG.md +++ b/crates/epaint/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to the epaint crate will be documented in this file. ## Unreleased +* ⚠️ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)). ## 0.19.0 - 2022-08-20 diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index a95df05b..5fdc82fe 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -1,5 +1,4 @@ -use std::collections::BTreeMap; -use std::sync::Arc; +use std::{collections::BTreeMap, sync::Arc}; use crate::{ mutex::{Mutex, MutexGuard}, @@ -271,7 +270,13 @@ impl Default for FontDefinitions { // Some good looking emojis. Use as first priority: font_data.insert( "NotoEmoji-Regular".to_owned(), - FontData::from_static(include_bytes!("../../fonts/NotoEmoji-Regular.ttf")), + FontData::from_static(include_bytes!("../../fonts/NotoEmoji-Regular.ttf")).tweak( + FontTweak { + scale: 0.81, // make it smaller + y_offset_factor: -0.2, // move it up + y_offset: 0.0, + }, + ), ); // Bigger emojis, and more. : @@ -279,7 +284,7 @@ impl Default for FontDefinitions { "emoji-icon-font".to_owned(), FontData::from_static(include_bytes!("../../fonts/emoji-icon-font.ttf")).tweak( FontTweak { - scale: 0.8, // make it smaller + scale: 0.88, // make it smaller y_offset_factor: 0.07, // move it down slightly y_offset: 0.0, }, @@ -526,6 +531,21 @@ impl FontsAndCache { // ---------------------------------------------------------------------------- +#[derive(Clone, Copy, Debug, PartialEq)] +struct HashableF32(f32); + +#[allow(clippy::derive_hash_xor_eq)] +impl std::hash::Hash for HashableF32 { + #[inline(always)] + fn hash(&self, state: &mut H) { + crate::f32_hash(state, self.0); + } +} + +impl Eq for HashableF32 {} + +// ---------------------------------------------------------------------------- + /// The collection of fonts used by `epaint`. /// /// Required in order to paint text. @@ -535,7 +555,7 @@ pub struct FontsImpl { definitions: FontDefinitions, atlas: Arc>, font_impl_cache: FontImplCache, - sized_family: ahash::HashMap<(u32, FontFamily), Font>, + sized_family: ahash::HashMap<(HashableF32, FontFamily), Font>, } impl FontsImpl { @@ -584,10 +604,9 @@ impl FontsImpl { /// Get the right font implementation from size and [`FontFamily`]. pub fn font(&mut self, font_id: &FontId) -> &mut Font { let FontId { size, family } = font_id; - let scale_in_pixels = self.font_impl_cache.scale_as_pixels(*size); self.sized_family - .entry((scale_in_pixels, family.clone())) + .entry((HashableF32(*size), family.clone())) .or_insert_with(|| { let fonts = &self.definitions.families.get(family); let fonts = fonts.unwrap_or_else(|| { @@ -596,7 +615,7 @@ impl FontsImpl { let fonts: Vec> = fonts .iter() - .map(|font_name| self.font_impl_cache.font_impl(scale_in_pixels, font_name)) + .map(|font_name| self.font_impl_cache.font_impl(*size, font_name)) .collect(); Font::new(fonts) @@ -699,23 +718,33 @@ impl FontImplCache { } } - #[inline] - pub fn scale_as_pixels(&self, scale_in_points: f32) -> u32 { - let scale_in_pixels = self.pixels_per_point * scale_in_points; + pub fn font_impl(&mut self, scale_in_points: f32, font_name: &str) -> Arc { + use ab_glyph::Font as _; - // Round to an even number of physical pixels to get even kerning. - // See https://github.com/emilk/egui/issues/382 - scale_in_pixels.round() as u32 - } - - pub fn font_impl(&mut self, scale_in_pixels: u32, font_name: &str) -> Arc { let (tweak, ab_glyph_font) = self .ab_glyph_fonts .get(font_name) .unwrap_or_else(|| panic!("No font data found for {:?}", font_name)) .clone(); - let scale_in_pixels = (scale_in_pixels as f32 * tweak.scale).round() as u32; + let scale_in_pixels = self.pixels_per_point * scale_in_points; + + // Scale the font properly (see https://github.com/emilk/egui/issues/2068). + let units_per_em = ab_glyph_font.units_per_em().unwrap_or_else(|| { + panic!( + "The font unit size of {:?} exceeds the expected range (16..=16384)", + font_name + ) + }); + let font_scaling = ab_glyph_font.height_unscaled() / units_per_em; + let scale_in_pixels = scale_in_pixels * font_scaling; + + // Tweak the scale as the user desired: + let scale_in_pixels = scale_in_pixels * tweak.scale; + + // Round to an even number of physical pixels to get even kerning. + // See https://github.com/emilk/egui/issues/382 + let scale_in_pixels = scale_in_pixels.round() as u32; let y_offset_points = { let scale_in_points = scale_in_pixels as f32 / self.pixels_per_point; diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index a690a1d0..e436db97 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -845,7 +845,7 @@ fn test_pre_cjk() { "日本語とEnglishの混在した文章".into(), super::TextFormat::default(), ); - layout_job.wrap.max_width = 100.0; + layout_job.wrap.max_width = 110.0; let galley = super::layout(&mut fonts, layout_job.into()); assert_eq!( galley diff --git a/examples/custom_font_style/src/main.rs b/examples/custom_font_style/src/main.rs index 781f1ff5..ba8a9c1f 100644 --- a/examples/custom_font_style/src/main.rs +++ b/examples/custom_font_style/src/main.rs @@ -18,13 +18,13 @@ fn configure_text_styles(ctx: &egui::Context) { let mut style = (*ctx.style()).clone(); style.text_styles = [ - (TextStyle::Heading, FontId::new(30.0, Proportional)), - (heading2(), FontId::new(25.0, Proportional)), - (heading3(), FontId::new(23.0, Proportional)), - (TextStyle::Body, FontId::new(18.0, Proportional)), - (TextStyle::Monospace, FontId::new(14.0, Proportional)), - (TextStyle::Button, FontId::new(14.0, Proportional)), - (TextStyle::Small, FontId::new(10.0, Proportional)), + (TextStyle::Heading, FontId::new(25.0, Proportional)), + (heading2(), FontId::new(22.0, Proportional)), + (heading3(), FontId::new(19.0, Proportional)), + (TextStyle::Body, FontId::new(16.0, Proportional)), + (TextStyle::Monospace, FontId::new(12.0, Proportional)), + (TextStyle::Button, FontId::new(12.0, Proportional)), + (TextStyle::Small, FontId::new(8.0, Proportional)), ] .into(); ctx.set_style(style); diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index ed1d931b..372fa7c7 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -70,7 +70,7 @@ fn custom_window_frame( rect.center_top() + vec2(0.0, height / 2.0), Align2::CENTER_CENTER, title, - FontId::proportional(height - 2.0), + FontId::proportional(height * 0.8), text_color, );