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 9cb01f62776..12c6ab23aba 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 c0003695db0..442fdc9fb47 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 e9a1305414c..47ac4458be7 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 6fb5727eb46..b83c0acbdab 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 3817e604fb7..ae3b347aac8 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 50f8c3944f5..c5fe3bb63e0 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 8d23b29460c..324a85a750b 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 80d793a8b19..f4307db95b7 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 80c0c8064a7..550e5c98bfd 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 56f57367fc0..86235dce923 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 a95df05bdcb..5fdc82fef80 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 a690a1d0abc..e436db970c1 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 781f1ff5a5d..ba8a9c1fa56 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 ed1d931be4a..372fa7c7765 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, );