refactor fonts: do all text layout via the Fonts struct

This commit is contained in:
Emil Ernerfeldt 2021-03-29 20:57:51 +02:00
parent 46425f1e38
commit ade41403b5
12 changed files with 112 additions and 52 deletions

View file

@ -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);

View file

@ -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

View file

@ -1245,9 +1245,8 @@ impl Ui {
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
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<R> {
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;

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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();

View file

@ -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()

View file

@ -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,

View file

@ -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
}

View file

@ -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<TextStyle> for Fonts {