From ade41403b520a26f92c9d09e89abaf1d5dfc767b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 29 Mar 2021 20:57:51 +0200 Subject: [PATCH] refactor fonts: do all text layout via the Fonts struct --- egui/src/containers/combo_box.rs | 3 +- egui/src/painter.rs | 10 +++-- egui/src/ui.rs | 10 ++--- egui/src/widgets/button.rs | 24 +++++------ egui/src/widgets/label.rs | 11 ++--- egui/src/widgets/plot.rs | 3 +- egui/src/widgets/selected_label.rs | 9 ++--- egui/src/widgets/slider.rs | 6 ++- egui/src/widgets/text_edit.rs | 20 +++++----- epaint/src/shape.rs | 3 +- epaint/src/text/font.rs | 1 + epaint/src/text/fonts.rs | 64 +++++++++++++++++++++++++++++- 12 files changed, 112 insertions(+), 52 deletions(-) diff --git a/egui/src/containers/combo_box.rs b/egui/src/containers/combo_box.rs index 7e64f076..d25a8012 100644 --- a/egui/src/containers/combo_box.rs +++ b/egui/src/containers/combo_box.rs @@ -180,8 +180,7 @@ fn combo_box( let icon_size = Vec2::splat(ui.spacing().icon_width); let text_style = TextStyle::Button; - let font = &ui.fonts()[text_style]; - let galley = font.layout_no_wrap(selected.into()); + let galley = ui.fonts().layout_no_wrap(text_style, selected.into()); let width = galley.size.x + ui.spacing().item_spacing.x + icon_size.x; let width = width.at_least(full_minimum_width); diff --git a/egui/src/painter.rs b/egui/src/painter.rs index 519c22cd..48fcf506 100644 --- a/egui/src/painter.rs +++ b/egui/src/painter.rs @@ -173,8 +173,9 @@ impl Painter { pub fn error(&self, pos: Pos2, text: impl std::fmt::Display) -> Rect { let text_style = TextStyle::Monospace; - let font = &self.fonts()[text_style]; - let galley = font.layout_multiline(format!("🔥 {}", text), f32::INFINITY); + let galley = + self.fonts() + .layout_multiline(text_style, format!("🔥 {}", text), f32::INFINITY); let rect = Align2::LEFT_TOP.anchor_rect(Rect::from_min_size(pos, galley.size)); let frame_rect = rect.expand(2.0); self.add(Shape::Rect { @@ -291,8 +292,9 @@ impl Painter { text_style: TextStyle, text_color: Color32, ) -> Rect { - let font = &self.fonts()[text_style]; - let galley = font.layout_multiline(text.into(), f32::INFINITY); + let galley = self + .fonts() + .layout_multiline(text_style, text.into(), f32::INFINITY); let rect = anchor.anchor_rect(Rect::from_min_size(pos, galley.size)); self.galley(rect.min, galley, text_style, text_color); rect diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 4cabdf15..7aee1c15 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -1245,9 +1245,8 @@ impl Ui { add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { self.wrap(|ui| { - let font = &ui.fonts()[text_style]; - let row_height = font.row_height(); - let space_width = font.glyph_width(' '); + let row_height = ui.fonts().row_height(text_style); + let space_width = ui.fonts().glyph_width(text_style, ' '); let spacing = ui.spacing_mut(); spacing.interact_size.y = row_height; spacing.item_spacing.x = space_width; @@ -1290,9 +1289,8 @@ impl Ui { add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { self.wrap(|ui| { - let font = &ui.fonts()[text_style]; - let row_height = font.row_height(); - let space_width = font.glyph_width(' '); + let row_height = ui.fonts().row_height(text_style); + let space_width = ui.fonts().glyph_width(text_style, ' '); let spacing = ui.spacing_mut(); spacing.interact_size.y = row_height; spacing.item_spacing.x = space_width; diff --git a/egui/src/widgets/button.rs b/egui/src/widgets/button.rs index 8326a8e9..f3339f5b 100644 --- a/egui/src/widgets/button.rs +++ b/egui/src/widgets/button.rs @@ -106,11 +106,11 @@ impl Button { } let total_extra = button_padding + button_padding; - let font = &ui.fonts()[text_style]; let galley = if ui.wrap_text() { - font.layout_multiline(text, ui.available_width() - total_extra.x) + ui.fonts() + .layout_multiline(text_style, text, ui.available_width() - total_extra.x) } else { - font.layout_no_wrap(text) + ui.fonts().layout_no_wrap(text_style, text) }; let mut desired_size = galley.size + 2.0 * button_padding; @@ -210,19 +210,18 @@ impl<'a> Widget for Checkbox<'a> { text_color, } = self; - let text_style = TextStyle::Button; - let font = &ui.fonts()[text_style]; - let spacing = &ui.spacing(); let icon_width = spacing.icon_width; let icon_spacing = ui.spacing().icon_spacing; let button_padding = spacing.button_padding; let total_extra = button_padding + vec2(icon_width + icon_spacing, 0.0) + button_padding; + let text_style = TextStyle::Button; let galley = if ui.wrap_text() { - font.layout_multiline(text, ui.available_width() - total_extra.x) + ui.fonts() + .layout_multiline(text_style, text, ui.available_width() - total_extra.x) } else { - font.layout_no_wrap(text) + ui.fonts().layout_no_wrap(text_style, text) }; let mut desired_size = total_extra + galley.size; @@ -322,18 +321,17 @@ impl Widget for RadioButton { text_color, } = self; - let text_style = TextStyle::Button; - let font = &ui.fonts()[text_style]; - let icon_width = ui.spacing().icon_width; let icon_spacing = ui.spacing().icon_spacing; let button_padding = ui.spacing().button_padding; let total_extra = button_padding + vec2(icon_width + icon_spacing, 0.0) + button_padding; + let text_style = TextStyle::Button; let galley = if ui.wrap_text() { - font.layout_multiline(text, ui.available_width() - total_extra.x) + ui.fonts() + .layout_multiline(text_style, text, ui.available_width() - total_extra.x) } else { - font.layout_no_wrap(text) + ui.fonts().layout_no_wrap(text_style, text) }; let mut desired_size = total_extra + galley.size; diff --git a/egui/src/widgets/label.rs b/egui/src/widgets/label.rs index b0ad54d9..4f6137f2 100644 --- a/egui/src/widgets/label.rs +++ b/egui/src/widgets/label.rs @@ -150,13 +150,14 @@ impl Label { pub fn layout_width(&self, ui: &Ui, max_width: f32) -> Galley { let text_style = self.text_style_or_default(ui.style()); - let font = &ui.fonts()[text_style]; let wrap_width = if self.should_wrap(ui) { max_width } else { f32::INFINITY }; - let galley = font.layout_multiline(self.text.clone(), wrap_width); // TODO: avoid clone + let galley = ui + .fonts() + .layout_multiline(text_style, self.text.clone(), wrap_width); // TODO: avoid clone self.valign_galley(ui, text_style, galley) } @@ -263,7 +264,7 @@ impl Label { -2.0 } else { let normal_text_heigth = ui.fonts()[TextStyle::Body].row_height(); - let font_height = ui.fonts()[text_style].row_height(); + let font_height = ui.fonts().row_height(text_style); (normal_text_heigth - font_height) / 2.0 - 1.0 // center // normal_text_heigth - font_height // align bottom @@ -293,8 +294,8 @@ impl Widget for Label { let first_row_indentation = max_width - ui.available_size_before_wrap().x; let text_style = self.text_style_or_default(ui.style()); - let font = &ui.fonts()[text_style]; - let mut galley = font.layout_multiline_with_indentation_and_max_width( + let mut galley = ui.fonts().layout_multiline_with_indentation_and_max_width( + text_style, self.text.clone(), first_row_indentation, max_width, diff --git a/egui/src/widgets/plot.rs b/egui/src/widgets/plot.rs index ac92b0bc..1539ea50 100644 --- a/egui/src/widgets/plot.rs +++ b/egui/src/widgets/plot.rs @@ -649,8 +649,7 @@ impl Prepared { let text = emath::round_to_decimals(value_main, 5).to_string(); // hack - let font = &ui.fonts()[text_style]; - let galley = font.layout_multiline(text, f32::INFINITY); + let galley = ui.fonts().layout_multiline(text_style, text, f32::INFINITY); let mut text_pos = pos_in_gui + vec2(1.0, -galley.size.y); diff --git a/egui/src/widgets/selected_label.rs b/egui/src/widgets/selected_label.rs index 49f5b906..e14acf50 100644 --- a/egui/src/widgets/selected_label.rs +++ b/egui/src/widgets/selected_label.rs @@ -40,16 +40,15 @@ impl Widget for SelectableLabel { fn ui(self, ui: &mut Ui) -> Response { let Self { selected, text } = self; - let text_style = TextStyle::Button; - let font = &ui.fonts()[text_style]; - let button_padding = ui.spacing().button_padding; let total_extra = button_padding + button_padding; + let text_style = TextStyle::Button; let galley = if ui.wrap_text() { - font.layout_multiline(text, ui.available_width() - total_extra.x) + ui.fonts() + .layout_multiline(text_style, text, ui.available_width() - total_extra.x) } else { - font.layout_no_wrap(text) + ui.fonts().layout_no_wrap(text_style, text) }; let mut desired_size = total_extra + galley.size; diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs index 0249ae15..e1df15c4 100644 --- a/egui/src/widgets/slider.rs +++ b/egui/src/widgets/slider.rs @@ -441,8 +441,10 @@ impl<'a> Slider<'a> { impl<'a> Widget for Slider<'a> { fn ui(mut self, ui: &mut Ui) -> Response { let text_style = TextStyle::Button; - let font = &ui.fonts()[text_style]; - let height = font.row_height().at_least(ui.spacing().interact_size.y); + let height = ui + .fonts() + .row_height(text_style) + .at_least(ui.spacing().interact_size.y); let old_value = self.get_value(); diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index 5ff57571..c8bea435 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -300,13 +300,13 @@ impl<'t> TextEdit<'t> { } = self; let text_style = text_style.unwrap_or_else(|| ui.style().body_text_style); - let font = &ui.fonts()[text_style]; - let line_spacing = font.row_height(); + let line_spacing = ui.fonts().row_height(text_style); let available_width = ui.available_width(); let mut galley = if multiline { - font.layout_multiline(text.clone(), available_width) + ui.fonts() + .layout_multiline(text_style, text.clone(), available_width) } else { - font.layout_single_line(text.clone()) + ui.fonts().layout_single_line(text_style, text.clone()) }; let desired_width = desired_width.unwrap_or_else(|| ui.spacing().text_edit_width); @@ -481,11 +481,11 @@ impl<'t> TextEdit<'t> { response.mark_changed(); // Layout again to avoid frame delay, and to keep `text` and `galley` in sync. - let font = &ui.fonts()[text_style]; galley = if multiline { - font.layout_multiline(text.clone(), available_width) + ui.fonts() + .layout_multiline(text_style, text.clone(), available_width) } else { - font.layout_single_line(text.clone()) + ui.fonts().layout_single_line(text_style, text.clone()) }; // Set cursorp using new galley: @@ -517,11 +517,11 @@ impl<'t> TextEdit<'t> { .galley(response.rect.min, galley, text_style, text_color); if text.is_empty() && !hint_text.is_empty() { - let font = &ui.fonts()[text_style]; let galley = if multiline { - font.layout_multiline(hint_text, available_width) + ui.fonts() + .layout_multiline(text_style, hint_text, available_width) } else { - font.layout_single_line(hint_text) + ui.fonts().layout_single_line(text_style, hint_text) }; let hint_text_color = ui.visuals().weak_text_color(); ui.painter() diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index e1580d3b..303f05a3 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -132,8 +132,7 @@ impl Shape { text_style: TextStyle, color: Color32, ) -> Self { - let font = &fonts[text_style]; - let galley = font.layout_multiline(text.into(), f32::INFINITY); + let galley = fonts.layout_multiline(text_style, text.into(), f32::INFINITY); let rect = anchor.anchor_rect(Rect::from_min_size(pos, galley.size)); Self::Text { pos: rect.min, diff --git a/epaint/src/text/font.rs b/epaint/src/text/font.rs index 9242f7a5..60d79b2c 100644 --- a/epaint/src/text/font.rs +++ b/epaint/src/text/font.rs @@ -222,6 +222,7 @@ impl Font { .and_then(|gi| gi.1.uv_rect) } + /// Width of this character in points. pub fn glyph_width(&self, c: char) -> f32 { self.glyph_info(c).1.advance_width } diff --git a/epaint/src/text/fonts.rs b/epaint/src/text/fonts.rs index b69300b7..a13642b8 100644 --- a/epaint/src/text/fonts.rs +++ b/epaint/src/text/fonts.rs @@ -6,7 +6,10 @@ use std::{ use crate::{ mutex::Mutex, - text::font::{Font, FontImpl}, + text::{ + font::{Font, FontImpl}, + Galley, + }, Texture, TextureAtlas, }; @@ -259,6 +262,65 @@ impl Fonts { buffered_texture.clone() } + + /// Width of this character in points. + pub fn glyph_width(&self, text_style: TextStyle, c: char) -> f32 { + self.fonts[&text_style].glyph_width(c) + } + + /// Height of one row of text. In points + pub fn row_height(&self, text_style: TextStyle) -> f32 { + self.fonts[&text_style].row_height() + } + + /// Always returns at least one row. + /// Will line break at `\n`. + pub fn layout_no_wrap(&self, text_style: TextStyle, text: String) -> Galley { + self.layout_multiline(text_style, text, f32::INFINITY) + } + + /// Typeset the given text onto one row. + /// Any `\n` will show up as the replacement character. + /// Always returns exactly one `Row` in the `Galley`. + /// + /// Most often you probably want `\n` to produce a new row, + /// and so [`Self::layout_no_wrap`] may be a better choice. + pub fn layout_single_line(&self, text_style: TextStyle, text: String) -> Galley { + self.fonts[&text_style].layout_single_line(text) + } + + /// Always returns at least one row. + /// Will wrap text at the given width. + pub fn layout_multiline( + &self, + text_style: TextStyle, + text: String, + max_width_in_points: f32, + ) -> Galley { + self.layout_multiline_with_indentation_and_max_width( + text_style, + text, + 0.0, + max_width_in_points, + ) + } + + /// * `first_row_indentation`: extra space before the very first character (in points). + /// * `max_width_in_points`: wrapping width. + /// Always returns at least one row. + pub fn layout_multiline_with_indentation_and_max_width( + &self, + text_style: TextStyle, + text: String, + first_row_indentation: f32, + max_width_in_points: f32, + ) -> Galley { + self.fonts[&text_style].layout_multiline_with_indentation_and_max_width( + text, + first_row_indentation, + max_width_in_points, + ) + } } impl std::ops::Index for Fonts {