Skip to content

Commit

Permalink
Add control of line height
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Sep 4, 2023
1 parent feb614e commit fc8be25
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 41 deletions.
21 changes: 18 additions & 3 deletions crates/egui/src/widget_text.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::borrow::Cow;
use std::sync::Arc;
use std::{borrow::Cow, sync::Arc};

use crate::{
style::WidgetVisuals, text::LayoutJob, Align, Color32, FontFamily, FontSelection, Galley, Pos2,
Expand All @@ -26,6 +25,7 @@ pub struct RichText {
text: String,
size: Option<f32>,
extra_letter_spacing: f32,
line_height: Option<f32>,
family: Option<FontFamily>,
text_style: Option<TextStyle>,
background_color: Color32,
Expand Down Expand Up @@ -110,6 +110,19 @@ impl RichText {
self
}

/// Explicit line height of the text in points.
///
/// This is the distance between the bottom row of two subsequent lines of text.
///
/// If `None` (the default), the line height is determined by the font.
///
/// Round to whole _pixels_ for crisp text.
#[inline]
pub fn line_height(mut self, line_height: Option<f32>) -> Self {
self.line_height = line_height;
self
}

/// Select the font family.
///
/// This overrides the value from [`Self::text_style`].
Expand Down Expand Up @@ -264,6 +277,7 @@ impl RichText {
text,
size,
extra_letter_spacing,
line_height,
family,
text_style,
background_color,
Expand Down Expand Up @@ -320,13 +334,14 @@ impl RichText {

let text_format = crate::text::TextFormat {
font_id,
extra_letter_spacing,
line_height,
color: text_color,
background: background_color,
italics,
underline,
strikethrough,
valign,
extra_letter_spacing,
};

let job = LayoutJob::single_section(text, text_format);
Expand Down
9 changes: 1 addition & 8 deletions crates/epaint/src/text/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@ pub struct GlyphInfo {
/// Unit: points.
pub ascent: f32,

/// row height computed from the font metrics.
///
/// Unit: points.
pub row_height: f32,

/// Texture coordinates.
pub uv_rect: UvRect,
}
Expand All @@ -65,7 +60,6 @@ impl Default for GlyphInfo {
id: ab_glyph::GlyphId(0),
advance_width: 0.0,
ascent: 0.0,
row_height: 0.0,
uv_rect: Default::default(),
}
}
Expand Down Expand Up @@ -250,7 +244,7 @@ impl FontImpl {
/ self.pixels_per_point
}

/// Height of one row of text. In points
/// Height of one row of text in points.
#[inline(always)]
pub fn row_height(&self) -> f32 {
self.height_in_points
Expand Down Expand Up @@ -312,7 +306,6 @@ impl FontImpl {
id: glyph_id,
advance_width: advance_width_in_points,
ascent: self.ascent,
row_height: self.row_height(),
uv_rect,
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/epaint/src/text/fonts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ impl FontsImpl {
self.font(font_id).has_glyphs(s)
}

/// Height of one row of text. In points
/// Height of one row of text in points.
fn row_height(&mut self, font_id: &FontId) -> f32 {
self.font(font_id).row_height()
}
Expand Down
28 changes: 17 additions & 11 deletions crates/epaint/src/text/text_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,14 @@ fn layout_section(
format,
} = section;
let font = fonts.font(&format.font_id);
let font_height = font.row_height();
let line_height = section
.format
.line_height
.unwrap_or_else(|| font.row_height());

let mut paragraph = out_paragraphs.last_mut().unwrap();
if paragraph.glyphs.is_empty() {
paragraph.empty_paragraph_height = font_height; // TODO(emilk): replace this hack with actually including `\n` in the glyphs?
paragraph.empty_paragraph_height = line_height; // TODO(emilk): replace this hack with actually including `\n` in the glyphs?
}

paragraph.cursor_x += leading_space;
Expand All @@ -128,7 +131,7 @@ fn layout_section(
if job.break_on_newline && chr == '\n' {
out_paragraphs.push(Paragraph::default());
paragraph = out_paragraphs.last_mut().unwrap();
paragraph.empty_paragraph_height = font_height; // TODO(emilk): replace this hack with actually including `\n` in the glyphs?
paragraph.empty_paragraph_height = line_height; // TODO(emilk): replace this hack with actually including `\n` in the glyphs?
} else {
let (font_impl, glyph_info) = font.glyph_info_and_font_impl(chr);
if let Some(font_impl) = font_impl {
Expand All @@ -141,7 +144,7 @@ fn layout_section(
paragraph.glyphs.push(Glyph {
chr,
pos: pos2(paragraph.cursor_x, f32::NAN),
size: vec2(glyph_info.advance_width, glyph_info.row_height),
size: vec2(glyph_info.advance_width, line_height),
ascent: glyph_info.ascent,
uv_rect: glyph_info.uv_rect,
section_index,
Expand Down Expand Up @@ -330,7 +333,10 @@ fn replace_last_glyph_with_overflow_character(

let section = &job.sections[last_glyph.section_index as usize];
let font = fonts.font(&section.format.font_id);
let font_height = font.row_height();
let line_height = section
.format
.line_height
.unwrap_or_else(|| font.row_height());

let prev_glyph_id = prev_glyph.map(|prev_glyph| {
let (_, prev_glyph_info) = font.glyph_info_and_font_impl(prev_glyph.chr);
Expand All @@ -350,7 +356,7 @@ fn replace_last_glyph_with_overflow_character(
// replace the glyph
last_glyph.chr = overflow_character;
let (font_impl, glyph_info) = font.glyph_info_and_font_impl(last_glyph.chr);
last_glyph.size = vec2(glyph_info.advance_width, font_height);
last_glyph.size = vec2(glyph_info.advance_width, line_height);
last_glyph.uv_rect = glyph_info.uv_rect;
last_glyph.ascent = glyph_info.ascent;

Expand Down Expand Up @@ -481,7 +487,7 @@ fn galley_from_rows(
let mut min_x: f32 = 0.0;
let mut max_x: f32 = 0.0;
for row in &mut rows {
let mut row_height = first_row_min_height.max(row.rect.height());
let mut line_height = first_row_min_height.max(row.rect.height());
let mut row_ascent = 0.0f32;
first_row_min_height = 0.0;

Expand All @@ -491,10 +497,10 @@ fn galley_from_rows(
.iter()
.max_by(|a, b| a.size.y.partial_cmp(&b.size.y).unwrap())
{
row_height = glyph.size.y;
line_height = glyph.size.y;
row_ascent = glyph.ascent;
}
row_height = point_scale.round_to_pixel(row_height);
line_height = point_scale.round_to_pixel(line_height);

// Now positions each glyph:
for glyph in &mut row.glyphs {
Expand All @@ -510,11 +516,11 @@ fn galley_from_rows(
}

row.rect.min.y = cursor_y;
row.rect.max.y = cursor_y + row_height;
row.rect.max.y = cursor_y + line_height;

min_x = min_x.min(row.rect.min.x);
max_x = max_x.max(row.rect.max.x);
cursor_y += row_height;
cursor_y += line_height;
cursor_y = point_scale.round_to_pixel(cursor_y);
}

Expand Down
52 changes: 34 additions & 18 deletions crates/epaint/src/text/text_layout_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ pub struct TextFormat {
/// Default: 0.0. Round to whole _pixels_ for crisp text.
pub extra_letter_spacing: f32,

/// Explicit line height of the text in points.
///
/// This is the distance between the bottom row of two subsequent lines of text.
///
/// If `None` (the default), the line height is determined by the font.
///
/// Round to whole _pixels_ for crisp text.
pub line_height: Option<f32>,

/// Text color
pub color: Color32,

Expand All @@ -249,12 +258,30 @@ pub struct TextFormat {
// TODO(emilk): lowered
}

impl Default for TextFormat {
#[inline]
fn default() -> Self {
Self {
font_id: FontId::default(),
extra_letter_spacing: 0.0,
line_height: None,
color: Color32::GRAY,
background: Color32::TRANSPARENT,
italics: false,
underline: Stroke::NONE,
strikethrough: Stroke::NONE,
valign: Align::BOTTOM,
}
}
}

impl std::hash::Hash for TextFormat {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let Self {
font_id,
extra_letter_spacing,
line_height,
color,
background,
italics,
Expand All @@ -264,6 +291,9 @@ impl std::hash::Hash for TextFormat {
} = self;
font_id.hash(state);
crate::f32_hash(state, *extra_letter_spacing);
if let Some(line_height) = *line_height {
crate::f32_hash(state, line_height);
}
color.hash(state);
background.hash(state);
italics.hash(state);
Expand All @@ -273,22 +303,6 @@ impl std::hash::Hash for TextFormat {
}
}

impl Default for TextFormat {
#[inline]
fn default() -> Self {
Self {
font_id: FontId::default(),
color: Color32::GRAY,
background: Color32::TRANSPARENT,
italics: false,
underline: Stroke::NONE,
strikethrough: Stroke::NONE,
valign: Align::BOTTOM,
extra_letter_spacing: 0.0,
}
}
}

impl TextFormat {
#[inline]
pub fn simple(font_id: FontId, color: Color32) -> Self {
Expand Down Expand Up @@ -517,10 +531,12 @@ pub struct Glyph {
/// `ascent` value from the font
pub ascent: f32,

/// Advance width and font row height.
/// Advance width and line height.
///
/// Does not control the visual size of the glyph (see [`Self::uv_rect`] for that).
pub size: Vec2,

/// Position of the glyph in the font texture, in texels.
/// Position and size of the glyph in the font texture, in texels.
pub uv_rect: UvRect,

/// Index into [`LayoutJob::sections`]. Decides color etc.
Expand Down

0 comments on commit fc8be25

Please sign in to comment.