mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 06:10:06 -04:00
Refactor replace_last_glyph_with_overflow_character
Muuuuuch nicer.
This commit is contained in:
@@ -439,26 +439,13 @@ impl Font<'_> {
|
|||||||
font_index_glyph_info
|
font_index_glyph_info
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn font_impl_and_glyph_info(
|
|
||||||
&mut self,
|
|
||||||
c: char,
|
|
||||||
) -> (Option<&mut FontImpl>, GlyphInfo) {
|
|
||||||
if self.cached_family.fonts.is_empty() {
|
|
||||||
return (None, self.cached_family.replacement_glyph.1);
|
|
||||||
}
|
|
||||||
let (key, glyph_info) = self.glyph_info(c);
|
|
||||||
let font_impl = self.fonts_by_id.get_mut(&key).expect("Nonexistent font ID");
|
|
||||||
(Some(font_impl), glyph_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn font_impl_and_glyph_alloc(
|
pub(crate) fn font_impl_and_glyph_alloc(
|
||||||
&mut self,
|
&mut self,
|
||||||
pixels_per_point: f32,
|
pixels_per_point: f32,
|
||||||
c: char,
|
c: char,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
) -> (Option<&FontImpl>, GlyphAllocation) {
|
) -> (Option<&mut FontImpl>, GlyphAllocation) {
|
||||||
if self.cached_family.fonts.is_empty() {
|
if self.cached_family.fonts.is_empty() {
|
||||||
return (None, Default::default());
|
return (None, Default::default());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use emath::{Align, GuiRounding as _, NumExt as _, Pos2, Rect, Vec2, pos2, vec2};
|
use emath::{Align, GuiRounding as _, NumExt as _, Pos2, Rect, Vec2, pos2, vec2};
|
||||||
|
|
||||||
use crate::{Color32, Mesh, Stroke, Vertex, stroke::PathStroke, text::font::Font};
|
use crate::{Color32, Mesh, Stroke, Vertex, stroke::PathStroke};
|
||||||
|
|
||||||
use super::{FontsImpl, Galley, Glyph, LayoutJob, LayoutSection, PlacedRow, Row, RowVisuals};
|
use super::{FontsImpl, Galley, Glyph, LayoutJob, LayoutSection, PlacedRow, Row, RowVisuals};
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ fn layout_section(
|
|||||||
let (font_impl, glyph_alloc) =
|
let (font_impl, glyph_alloc) =
|
||||||
font.font_impl_and_glyph_alloc(pixels_per_point, chr, font_size);
|
font.font_impl_and_glyph_alloc(pixels_per_point, chr, font_size);
|
||||||
|
|
||||||
if let (Some(font_impl), Some(last_glyph_id)) = (font_impl, last_glyph_id) {
|
if let (Some(font_impl), Some(last_glyph_id)) = (&font_impl, last_glyph_id) {
|
||||||
paragraph.cursor_x += font_impl.pair_kerning(
|
paragraph.cursor_x += font_impl.pair_kerning(
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
last_glyph_id,
|
last_glyph_id,
|
||||||
@@ -192,8 +192,8 @@ fn layout_section(
|
|||||||
pos: pos2(paragraph.cursor_x, f32::NAN),
|
pos: pos2(paragraph.cursor_x, f32::NAN),
|
||||||
advance_width: glyph_alloc.advance_width,
|
advance_width: glyph_alloc.advance_width,
|
||||||
line_height,
|
line_height,
|
||||||
font_impl_height: font_impl.map_or(0.0, |f| f.row_height(font_size)),
|
font_impl_height: font_impl.as_ref().map_or(0.0, |f| f.row_height(font_size)),
|
||||||
font_impl_ascent: font_impl.map_or(0.0, |f| f.ascent(font_size)),
|
font_impl_ascent: font_impl.as_ref().map_or(0.0, |f| f.ascent(font_size)),
|
||||||
font_height: font.row_height(font_size),
|
font_height: font.row_height(font_size),
|
||||||
font_ascent: font.ascent(font_size),
|
font_ascent: font.ascent(font_size),
|
||||||
uv_rect: glyph_alloc.uv_rect,
|
uv_rect: glyph_alloc.uv_rect,
|
||||||
@@ -416,177 +416,84 @@ fn replace_last_glyph_with_overflow_character(
|
|||||||
job: &LayoutJob,
|
job: &LayoutJob,
|
||||||
row: &mut Row,
|
row: &mut Row,
|
||||||
) {
|
) {
|
||||||
fn row_width(row: &Row) -> f32 {
|
|
||||||
if let (Some(first), Some(last)) = (row.glyphs.first(), row.glyphs.last()) {
|
|
||||||
last.max_x() - first.pos.x
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn row_height(section: &LayoutSection, font: &Font<'_>, font_size: f32) -> f32 {
|
|
||||||
section
|
|
||||||
.format
|
|
||||||
.line_height
|
|
||||||
.unwrap_or_else(|| font.row_height(font_size))
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(overflow_character) = job.wrap.overflow_character else {
|
let Some(overflow_character) = job.wrap.overflow_character else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// We always try to just append the character first:
|
let mut section_index = row
|
||||||
if let Some(last_glyph) = row.glyphs.last() {
|
.glyphs
|
||||||
let section_index = last_glyph.section_index;
|
.last()
|
||||||
let section = &job.sections[section_index as usize];
|
.map(|g| g.section_index)
|
||||||
let mut font = fonts.font(§ion.format.font_id.family);
|
.unwrap_or(row.section_index_at_start);
|
||||||
let font_size = section.format.font_id.size;
|
|
||||||
let line_height = row_height(section, &font, font_size);
|
|
||||||
|
|
||||||
let (_, last_glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr);
|
|
||||||
|
|
||||||
let mut x = last_glyph.pos.x + last_glyph.advance_width;
|
|
||||||
|
|
||||||
let (font_impl, replacement_glyph_alloc) =
|
|
||||||
font.font_impl_and_glyph_alloc(pixels_per_point, overflow_character, font_size);
|
|
||||||
|
|
||||||
// Kerning:
|
|
||||||
x += section.format.extra_letter_spacing;
|
|
||||||
if let Some(font_impl) = font_impl {
|
|
||||||
if let Some(last_glyph_id) = last_glyph_info.id {
|
|
||||||
x += font_impl.pair_kerning(
|
|
||||||
pixels_per_point,
|
|
||||||
last_glyph_id,
|
|
||||||
replacement_glyph_alloc.id,
|
|
||||||
section.format.font_id.size,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
row.glyphs.push(Glyph {
|
|
||||||
chr: overflow_character,
|
|
||||||
pos: pos2(x, f32::NAN),
|
|
||||||
advance_width: replacement_glyph_alloc.advance_width,
|
|
||||||
line_height,
|
|
||||||
font_impl_height: font_impl.map_or(0.0, |f| f.row_height(font_size)),
|
|
||||||
font_impl_ascent: font_impl.map_or(0.0, |f| f.ascent(font_size)),
|
|
||||||
font_height: font.row_height(font_size),
|
|
||||||
font_ascent: font.ascent(font_size),
|
|
||||||
uv_rect: replacement_glyph_alloc.uv_rect,
|
|
||||||
section_index,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let section_index = row.section_index_at_start;
|
|
||||||
let section = &job.sections[section_index as usize];
|
|
||||||
let mut font = fonts.font(§ion.format.font_id.family);
|
|
||||||
let font_size = section.format.font_id.size;
|
|
||||||
let line_height = row_height(section, &font, font_size);
|
|
||||||
|
|
||||||
let x = 0.0; // TODO(emilk): heed paragraph leading_space 😬
|
|
||||||
|
|
||||||
let (mut font_impl, replacement_glyph_alloc) =
|
|
||||||
font.font_impl_and_glyph_alloc(pixels_per_point, overflow_character, font_size);
|
|
||||||
|
|
||||||
row.glyphs.push(Glyph {
|
|
||||||
chr: overflow_character,
|
|
||||||
pos: pos2(x, f32::NAN),
|
|
||||||
advance_width: replacement_glyph_alloc.advance_width,
|
|
||||||
line_height,
|
|
||||||
font_impl_height: font_impl.as_mut().map_or(0.0, |f| f.row_height(font_size)),
|
|
||||||
font_impl_ascent: font_impl.as_mut().map_or(0.0, |f| f.ascent(font_size)),
|
|
||||||
font_height: font.row_height(font_size),
|
|
||||||
font_ascent: font.ascent(font_size),
|
|
||||||
uv_rect: replacement_glyph_alloc.uv_rect,
|
|
||||||
section_index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if row_width(row) <= job.effective_wrap_width() || row.glyphs.len() == 1 {
|
|
||||||
return; // we are done
|
|
||||||
}
|
|
||||||
|
|
||||||
// We didn't fit it. Remove it again…
|
|
||||||
row.glyphs.pop();
|
|
||||||
|
|
||||||
// …then go into a loop where we replace the last character with the overflow character
|
|
||||||
// until we fit within the max_width:
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (prev_glyph, last_glyph) = match row.glyphs.as_mut_slice() {
|
let section = &job.sections[section_index as usize];
|
||||||
[.., prev, last] => (Some(prev), last),
|
|
||||||
[.., last] => (None, last),
|
|
||||||
_ => {
|
|
||||||
unreachable!("We've already explicitly handled the empty row");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let section = &job.sections[last_glyph.section_index as usize];
|
|
||||||
let extra_letter_spacing = section.format.extra_letter_spacing;
|
let extra_letter_spacing = section.format.extra_letter_spacing;
|
||||||
let mut font = fonts.font(§ion.format.font_id.family);
|
let mut font = fonts.font(§ion.format.font_id.family);
|
||||||
let font_size = section.format.font_id.size;
|
let font_size = section.format.font_id.size;
|
||||||
|
|
||||||
if let Some(prev_glyph) = prev_glyph {
|
let (mut font_impl, replacement_glyph_alloc) =
|
||||||
let prev_glyph_id = font
|
font.font_impl_and_glyph_alloc(pixels_per_point, overflow_character, font_size);
|
||||||
.font_impl_and_glyph_alloc(pixels_per_point, prev_glyph.chr, font_size)
|
|
||||||
.1
|
|
||||||
.id;
|
|
||||||
|
|
||||||
// Undo kerning with previous glyph:
|
let overflow_glyph_x = if let Some(prev_glyph) = row.glyphs.last() {
|
||||||
let (font_impl, glyph_alloc) =
|
// Kern the overflow character properly
|
||||||
font.font_impl_and_glyph_alloc(pixels_per_point, last_glyph.chr, font_size);
|
let pair_kerning = font_impl
|
||||||
last_glyph.pos.x -= extra_letter_spacing;
|
.as_mut()
|
||||||
if let Some(font_impl) = font_impl {
|
.map(|font_impl| {
|
||||||
last_glyph.pos.x -= font_impl.pair_kerning(
|
if let (Some(prev_glyph_id), Some(overflow_glyph_id)) = (
|
||||||
pixels_per_point,
|
font_impl.glyph_info(prev_glyph.chr).and_then(|g| g.id),
|
||||||
prev_glyph_id,
|
font_impl.glyph_info(overflow_character).and_then(|g| g.id),
|
||||||
glyph_alloc.id,
|
) {
|
||||||
font_size,
|
font_impl.pair_kerning(
|
||||||
);
|
pixels_per_point,
|
||||||
}
|
prev_glyph_id,
|
||||||
|
overflow_glyph_id,
|
||||||
|
font_size,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Replace the glyph:
|
prev_glyph.max_x() + extra_letter_spacing + pair_kerning
|
||||||
last_glyph.chr = overflow_character;
|
|
||||||
let (font_impl, glyph_alloc) =
|
|
||||||
font.font_impl_and_glyph_alloc(pixels_per_point, last_glyph.chr, font_size);
|
|
||||||
last_glyph.advance_width = glyph_alloc.advance_width;
|
|
||||||
last_glyph.font_impl_ascent = font_impl.map_or(0.0, |f| f.ascent(font_size));
|
|
||||||
last_glyph.font_impl_height = font_impl.map_or(0.0, |f| f.row_height(font_size));
|
|
||||||
last_glyph.uv_rect = glyph_alloc.uv_rect;
|
|
||||||
|
|
||||||
// Reapply kerning:
|
|
||||||
last_glyph.pos.x += extra_letter_spacing;
|
|
||||||
if let Some(font_impl) = font_impl {
|
|
||||||
last_glyph.pos.x += font_impl.pair_kerning(
|
|
||||||
pixels_per_point,
|
|
||||||
prev_glyph_id,
|
|
||||||
glyph_alloc.id,
|
|
||||||
font_size,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we're within width budget:
|
|
||||||
if row_width(row) <= job.effective_wrap_width() || row.glyphs.len() == 1 {
|
|
||||||
return; // We are done
|
|
||||||
}
|
|
||||||
|
|
||||||
// We didn't fit - pop the last glyph and try again.
|
|
||||||
row.glyphs.pop();
|
|
||||||
} else {
|
} else {
|
||||||
let Some(section) = &job.sections.get(last_glyph.section_index as usize) else {
|
0.0 // TODO(emilk): heed paragraph leading_space 😬
|
||||||
return;
|
};
|
||||||
};
|
|
||||||
let mut font = fonts.font(§ion.format.font_id.family);
|
// Check if we're within width budget:
|
||||||
let font_size = section.format.font_id.size;
|
if overflow_glyph_x + replacement_glyph_alloc.advance_width <= job.effective_wrap_width()
|
||||||
// Just replace and be done with it.
|
|| row.glyphs.is_empty()
|
||||||
last_glyph.chr = overflow_character;
|
{
|
||||||
let (font_impl, glyph_alloc) =
|
// we are done
|
||||||
font.font_impl_and_glyph_alloc(pixels_per_point, last_glyph.chr, font_size);
|
|
||||||
last_glyph.advance_width = glyph_alloc.advance_width;
|
// We need to calculate these first since `font_impl` is mutably borrowed from `font`, which is later used
|
||||||
last_glyph.font_impl_ascent = font_impl.map_or(0.0, |f| f.ascent(font_size));
|
// to calculate the row height
|
||||||
last_glyph.font_impl_height = font_impl.map_or(0.0, |f| f.row_height(font_size));
|
let font_impl_height = font_impl.as_mut().map_or(0.0, |f| f.row_height(font_size));
|
||||||
last_glyph.uv_rect = glyph_alloc.uv_rect;
|
let font_impl_ascent = font_impl.as_mut().map_or(0.0, |f| f.ascent(font_size));
|
||||||
|
let font_height = font.row_height(font_size);
|
||||||
|
let line_height = section.format.line_height.unwrap_or(font_height);
|
||||||
|
|
||||||
|
row.glyphs.push(Glyph {
|
||||||
|
chr: overflow_character,
|
||||||
|
pos: pos2(overflow_glyph_x, f32::NAN),
|
||||||
|
advance_width: replacement_glyph_alloc.advance_width,
|
||||||
|
line_height,
|
||||||
|
font_impl_height,
|
||||||
|
font_impl_ascent,
|
||||||
|
font_height,
|
||||||
|
font_ascent: font.ascent(font_size),
|
||||||
|
uv_rect: replacement_glyph_alloc.uv_rect,
|
||||||
|
section_index,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We didn't fit - pop the last glyph and try again.
|
||||||
|
if let Some(last_glyph) = row.glyphs.pop() {
|
||||||
|
section_index = last_glyph.section_index;
|
||||||
|
} else {
|
||||||
|
section_index = row.section_index_at_start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user