mirror of
https://github.com/emilk/egui.git
synced 2026-06-28 07:23:13 -04:00
Let Fonts handle multiple pixels_per_point
This commit is contained in:
@@ -2,15 +2,15 @@
|
||||
|
||||
use std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration};
|
||||
|
||||
use emath::{GuiRounding as _, OrderedFloat};
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::{
|
||||
ClippedPrimitive, ClippedShape, Color32, ImageData, ImageDelta, Pos2, Rect, StrokeKind,
|
||||
TessellationOptions, TextureAtlas, TextureId, Vec2,
|
||||
ClippedPrimitive, ClippedShape, Color32, ImageData, Pos2, Rect, StrokeKind,
|
||||
TessellationOptions, TextureId, Vec2,
|
||||
emath::{self, TSTransform},
|
||||
mutex::RwLock,
|
||||
stats::PaintStats,
|
||||
tessellator,
|
||||
text::{FontInsert, FontPriority, Fonts},
|
||||
text::{FontInsert, FontPriority, Fonts, FontsView},
|
||||
vec2,
|
||||
};
|
||||
|
||||
@@ -406,12 +406,7 @@ impl ViewportRepaintInfo {
|
||||
|
||||
#[derive(Default)]
|
||||
struct ContextImpl {
|
||||
/// Since we could have multiple viewports across multiple monitors with
|
||||
/// different `pixels_per_point`, we need a `Fonts` instance for each unique
|
||||
/// `pixels_per_point`.
|
||||
/// This is because the `Fonts` depend on `pixels_per_point` for the font atlas
|
||||
/// as well as kerning, font sizes, etc.
|
||||
fonts: std::collections::BTreeMap<OrderedFloat<f32>, Fonts>,
|
||||
fonts: Option<Fonts>,
|
||||
font_definitions: FontDefinitions,
|
||||
|
||||
memory: Memory,
|
||||
@@ -575,12 +570,11 @@ impl ContextImpl {
|
||||
fn update_fonts_mut(&mut self) {
|
||||
profiling::function_scope!();
|
||||
let input = &self.viewport().input;
|
||||
let pixels_per_point = input.pixels_per_point();
|
||||
let max_texture_side = input.max_texture_side;
|
||||
|
||||
if let Some(font_definitions) = self.memory.new_font_definitions.take() {
|
||||
// New font definition loaded, so we need to reload all fonts.
|
||||
self.fonts.clear();
|
||||
self.fonts = None;
|
||||
self.font_definitions = font_definitions;
|
||||
#[cfg(feature = "log")]
|
||||
log::trace!("Loading new font definitions");
|
||||
@@ -589,7 +583,7 @@ impl ContextImpl {
|
||||
if !self.memory.add_fonts.is_empty() {
|
||||
let fonts = self.memory.add_fonts.drain(..);
|
||||
for font in fonts {
|
||||
self.fonts.clear(); // recreate all the fonts
|
||||
self.fonts = None; // recreate all the fonts
|
||||
for family in font.families {
|
||||
let fam = self
|
||||
.font_definitions
|
||||
@@ -614,26 +608,22 @@ impl ContextImpl {
|
||||
|
||||
let mut is_new = false;
|
||||
|
||||
let fonts = self
|
||||
.fonts
|
||||
.entry(pixels_per_point.into())
|
||||
.or_insert_with(|| {
|
||||
#[cfg(feature = "log")]
|
||||
log::trace!("Creating new Fonts for pixels_per_point={pixels_per_point}");
|
||||
let fonts = self.fonts.get_or_insert_with(|| {
|
||||
#[cfg(feature = "log")]
|
||||
log::trace!("Creating new Fonts");
|
||||
|
||||
is_new = true;
|
||||
profiling::scope!("Fonts::new");
|
||||
Fonts::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
text_alpha_from_coverage,
|
||||
self.font_definitions.clone(),
|
||||
)
|
||||
});
|
||||
is_new = true;
|
||||
profiling::scope!("Fonts::new");
|
||||
Fonts::new(
|
||||
max_texture_side,
|
||||
text_alpha_from_coverage,
|
||||
self.font_definitions.clone(),
|
||||
)
|
||||
});
|
||||
|
||||
{
|
||||
profiling::scope!("Fonts::begin_pass");
|
||||
fonts.begin_pass(pixels_per_point, max_texture_side, text_alpha_from_coverage);
|
||||
fonts.begin_pass(max_texture_side, text_alpha_from_coverage);
|
||||
}
|
||||
|
||||
if is_new && self.memory.options.preload_font_glyphs {
|
||||
@@ -1052,13 +1042,14 @@ impl Context {
|
||||
/// Not valid until first call to [`Context::run()`].
|
||||
/// That's because since we don't know the proper `pixels_per_point` until then.
|
||||
#[inline]
|
||||
pub fn fonts<R>(&self, reader: impl FnOnce(&Fonts) -> R) -> R {
|
||||
pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
|
||||
self.write(move |ctx| {
|
||||
let pixels_per_point = ctx.pixels_per_point();
|
||||
reader(
|
||||
ctx.fonts
|
||||
.get(&pixels_per_point.into())
|
||||
.expect("No fonts available until first call to Context::run()"),
|
||||
&ctx.fonts
|
||||
.as_mut()
|
||||
.expect("No fonts available until first call to Context::run()")
|
||||
.with_pixels_per_point(pixels_per_point),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -1068,13 +1059,15 @@ impl Context {
|
||||
/// Not valid until first call to [`Context::run()`].
|
||||
/// That's because since we don't know the proper `pixels_per_point` until then.
|
||||
#[inline]
|
||||
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut Fonts) -> R) -> R {
|
||||
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
|
||||
self.write(move |ctx| {
|
||||
let pixels_per_point = ctx.pixels_per_point();
|
||||
reader(
|
||||
ctx.fonts
|
||||
.get_mut(&pixels_per_point.into())
|
||||
.expect("No fonts available until first call to Context::run()"),
|
||||
&mut ctx
|
||||
.fonts
|
||||
.as_mut()
|
||||
.expect("No fonts available until first call to Context::run()")
|
||||
.with_pixels_per_point(pixels_per_point),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -1938,14 +1931,12 @@ impl Context {
|
||||
pub fn set_fonts(&self, font_definitions: FontDefinitions) {
|
||||
profiling::function_scope!();
|
||||
|
||||
let pixels_per_point = self.pixels_per_point();
|
||||
|
||||
let mut update_fonts = true;
|
||||
|
||||
self.read(|ctx| {
|
||||
if let Some(current_fonts) = ctx.fonts.get(&pixels_per_point.into()) {
|
||||
if let Some(current_fonts) = ctx.fonts.as_ref() {
|
||||
// NOTE: this comparison is expensive since it checks TTF data for equality
|
||||
if current_fonts.fonts.definitions() == &font_definitions {
|
||||
if current_fonts.definitions() == &font_definitions {
|
||||
update_fonts = false; // no need to update
|
||||
}
|
||||
}
|
||||
@@ -1966,14 +1957,11 @@ impl Context {
|
||||
pub fn add_font(&self, new_font: FontInsert) {
|
||||
profiling::function_scope!();
|
||||
|
||||
let pixels_per_point = self.pixels_per_point();
|
||||
|
||||
let mut update_fonts = true;
|
||||
|
||||
self.read(|ctx| {
|
||||
if let Some(current_fonts) = ctx.fonts.get(&pixels_per_point.into()) {
|
||||
if let Some(current_fonts) = ctx.fonts.as_ref() {
|
||||
if current_fonts
|
||||
.fonts
|
||||
.definitions()
|
||||
.font_data
|
||||
.contains_key(&new_font.name)
|
||||
@@ -2466,31 +2454,12 @@ impl ContextImpl {
|
||||
|
||||
self.memory.end_pass(&viewport.this_pass.used_ids);
|
||||
|
||||
let num_font_envs = self.fonts.len();
|
||||
if let Some(fonts) = self.fonts.get_mut(&pixels_per_point.into()) {
|
||||
if let Some(fonts) = self.fonts.as_mut() {
|
||||
let tex_mngr = &mut self.tex_manager.0.write();
|
||||
if let Some(font_image_delta) = fonts.font_image_delta() {
|
||||
// A partial font atlas update, e.g. a new glyph has been entered.
|
||||
tex_mngr.set(TextureId::default(), font_image_delta);
|
||||
}
|
||||
|
||||
if 1 < num_font_envs {
|
||||
// We have multiple different `pixels_per_point`,
|
||||
// e.g. because we have many viewports spread across
|
||||
// monitors with different DPI scaling.
|
||||
// All viewports share the same texture namespace and renderer,
|
||||
// so the all use `TextureId::default()` for the font texture.
|
||||
// This is a problem.
|
||||
// We solve this with a hack: we always upload the full font atlas
|
||||
// every frame, for all viewports.
|
||||
// This ensures it is up-to-date, solving
|
||||
// https://github.com/emilk/egui/issues/3664
|
||||
// at the cost of a lot of performance.
|
||||
// (This will override any smaller delta that was uploaded above.)
|
||||
profiling::scope!("full_font_atlas_update");
|
||||
let full_delta = ImageDelta::full(fonts.image(), TextureAtlas::texture_options());
|
||||
tex_mngr.set(TextureId::default(), full_delta);
|
||||
}
|
||||
}
|
||||
|
||||
// Inform the backend of all textures that have been updated (including font atlas).
|
||||
@@ -2633,24 +2602,6 @@ impl ContextImpl {
|
||||
self.memory.set_viewport_id(viewport_id);
|
||||
}
|
||||
|
||||
let active_pixels_per_point: std::collections::BTreeSet<OrderedFloat<f32>> = self
|
||||
.viewports
|
||||
.values()
|
||||
.map(|v| v.input.pixels_per_point.into())
|
||||
.collect();
|
||||
self.fonts.retain(|pixels_per_point, _| {
|
||||
if active_pixels_per_point.contains(pixels_per_point) {
|
||||
true
|
||||
} else {
|
||||
#[cfg(feature = "log")]
|
||||
log::trace!(
|
||||
"Freeing Fonts with pixels_per_point={} because it is no longer needed",
|
||||
pixels_per_point.into_inner()
|
||||
);
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
platform_output.num_completed_passes += 1;
|
||||
|
||||
FullOutput {
|
||||
@@ -2682,7 +2633,7 @@ impl Context {
|
||||
|
||||
self.write(|ctx| {
|
||||
let tessellation_options = ctx.memory.options.tessellation_options;
|
||||
let texture_atlas = if let Some(fonts) = ctx.fonts.get(&pixels_per_point.into()) {
|
||||
let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {
|
||||
fonts.texture_atlas()
|
||||
} else {
|
||||
#[cfg(feature = "log")]
|
||||
@@ -2691,7 +2642,6 @@ impl Context {
|
||||
.iter()
|
||||
.next()
|
||||
.expect("No fonts loaded")
|
||||
.1
|
||||
.texture_atlas()
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::{
|
||||
CircleShape, ClippedShape, CornerRadius, PathStroke, RectShape, Shape, Stroke, StrokeKind,
|
||||
text::{Fonts, Galley, LayoutJob},
|
||||
text::{FontsView, Galley, LayoutJob},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -145,7 +145,7 @@ impl Painter {
|
||||
///
|
||||
/// See [`Context`] documentation for how locks work.
|
||||
#[inline]
|
||||
pub fn fonts<R>(&self, reader: impl FnOnce(&Fonts) -> R) -> R {
|
||||
pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
|
||||
self.ctx.fonts(reader)
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ impl Painter {
|
||||
///
|
||||
/// See [`Context`] documentation for how locks work.
|
||||
#[inline]
|
||||
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut Fonts) -> R) -> R {
|
||||
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
|
||||
self.ctx.fonts_mut(reader)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::mutex::RwLock;
|
||||
use epaint::text::FontsView;
|
||||
use std::{any::Any, hash::Hash, sync::Arc};
|
||||
|
||||
use crate::ClosableTag;
|
||||
@@ -16,9 +17,7 @@ use crate::{
|
||||
WidgetRect, WidgetText,
|
||||
containers::{CollapsingHeader, CollapsingResponse, Frame},
|
||||
ecolor::Hsva,
|
||||
emath, epaint,
|
||||
epaint::text::Fonts,
|
||||
grid,
|
||||
emath, epaint, grid,
|
||||
layout::{Direction, Layout},
|
||||
pass_state,
|
||||
placer::Placer,
|
||||
@@ -849,13 +848,13 @@ impl Ui {
|
||||
|
||||
/// Read-only access to [`Fonts`].
|
||||
#[inline]
|
||||
pub fn fonts<R>(&self, reader: impl FnOnce(&Fonts) -> R) -> R {
|
||||
pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
|
||||
self.ctx().fonts(reader)
|
||||
}
|
||||
|
||||
/// Read-write access to [`Fonts`].
|
||||
#[inline]
|
||||
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut Fonts) -> R) -> R {
|
||||
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
|
||||
self.ctx().fonts_mut(reader)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ impl RichText {
|
||||
/// Read the font height of the selected text style.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
pub fn font_height(&self, fonts: &mut epaint::Fonts, style: &Style) -> f32 {
|
||||
pub fn font_height(&self, fonts: &mut epaint::FontsView<'_>, style: &Style) -> f32 {
|
||||
let mut font_id = self.text_style.as_ref().map_or_else(
|
||||
|| FontSelection::Default.resolve(style),
|
||||
|text_style| text_style.resolve(style),
|
||||
@@ -676,7 +676,7 @@ impl WidgetText {
|
||||
}
|
||||
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
pub(crate) fn font_height(&self, fonts: &mut epaint::Fonts, style: &Style) -> f32 {
|
||||
pub(crate) fn font_height(&self, fonts: &mut epaint::FontsView<'_>, style: &Style) -> f32 {
|
||||
match self {
|
||||
Self::Text(_) => fonts.row_height(&FontSelection::Default.resolve(style)),
|
||||
Self::RichText(text) => text.font_height(fonts, style),
|
||||
|
||||
@@ -166,7 +166,6 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
let font_id = egui::FontId::default();
|
||||
let text_color = egui::Color32::WHITE;
|
||||
let mut fonts = egui::epaint::text::Fonts::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
egui::epaint::AlphaFromCoverage::default(),
|
||||
egui::FontDefinitions::default(),
|
||||
@@ -182,13 +181,13 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
text_color,
|
||||
wrap_width,
|
||||
);
|
||||
layout(&mut fonts.fonts, job.into())
|
||||
layout(&mut fonts.fonts, job.into(), pixels_per_point)
|
||||
});
|
||||
});
|
||||
}
|
||||
c.bench_function("text_layout_cached", |b| {
|
||||
b.iter(|| {
|
||||
fonts.layout(
|
||||
fonts.with_pixels_per_point(pixels_per_point).layout(
|
||||
LOREM_IPSUM_LONG.to_owned(),
|
||||
font_id.clone(),
|
||||
text_color,
|
||||
@@ -210,22 +209,28 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
||||
let mut rng = rand::rng();
|
||||
b.iter(|| {
|
||||
fonts.begin_pass(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
egui::epaint::AlphaFromCoverage::default(),
|
||||
);
|
||||
fonts.begin_pass(max_texture_side, egui::epaint::AlphaFromCoverage::default());
|
||||
|
||||
// Delete a random character, simulating a user making an edit in a long file:
|
||||
let mut new_string = string.clone();
|
||||
let idx = rng.random_range(0..string.len());
|
||||
new_string.remove(idx);
|
||||
|
||||
fonts.layout(new_string, font_id.clone(), text_color, wrap_width);
|
||||
fonts.with_pixels_per_point(pixels_per_point).layout(
|
||||
new_string,
|
||||
font_id.clone(),
|
||||
text_color,
|
||||
wrap_width,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), font_id, text_color, wrap_width);
|
||||
let galley = fonts.with_pixels_per_point(pixels_per_point).layout(
|
||||
LOREM_IPSUM_LONG.to_owned(),
|
||||
font_id,
|
||||
text_color,
|
||||
wrap_width,
|
||||
);
|
||||
let font_image_size = fonts.font_image_size();
|
||||
let prepared_discs = fonts.texture_atlas().prepared_discs();
|
||||
let mut tessellator = egui::epaint::Tessellator::new(
|
||||
|
||||
@@ -62,7 +62,7 @@ pub use self::{
|
||||
stats::PaintStats,
|
||||
stroke::{PathStroke, Stroke, StrokeKind},
|
||||
tessellator::{TessellationOptions, Tessellator},
|
||||
text::{FontFamily, FontId, Fonts, Galley},
|
||||
text::{FontFamily, FontId, Fonts, FontsView, Galley},
|
||||
texture_atlas::TextureAtlas,
|
||||
texture_handle::TextureHandle,
|
||||
textures::TextureManager,
|
||||
|
||||
@@ -7,7 +7,7 @@ use emath::{Align2, Pos2, Rangef, Rect, TSTransform, Vec2, pos2};
|
||||
use crate::{
|
||||
Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId,
|
||||
stroke::PathStroke,
|
||||
text::{FontId, Fonts, Galley},
|
||||
text::{FontId, FontsView, Galley},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -299,7 +299,7 @@ impl Shape {
|
||||
|
||||
#[expect(clippy::needless_pass_by_value)]
|
||||
pub fn text(
|
||||
fonts: &mut Fonts,
|
||||
fonts: &mut FontsView<'_>,
|
||||
pos: Pos2,
|
||||
anchor: Align2,
|
||||
text: impl ToString,
|
||||
|
||||
@@ -182,7 +182,6 @@ mod tests {
|
||||
#[test]
|
||||
fn text_bounding_box_under_rotation() {
|
||||
let mut fonts = Fonts::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
@@ -190,7 +189,7 @@ mod tests {
|
||||
let font = FontId::monospace(12.0);
|
||||
|
||||
let mut t = crate::Shape::text(
|
||||
&mut fonts,
|
||||
&mut fonts.with_pixels_per_point(1.0),
|
||||
Pos2::ZERO,
|
||||
emath::Align2::CENTER_CENTER,
|
||||
"testing123",
|
||||
|
||||
@@ -155,7 +155,7 @@ impl FontImpl {
|
||||
}
|
||||
|
||||
/// `\n` will result in `None`
|
||||
fn glyph_info(&mut self, c: char) -> Option<GlyphInfo> {
|
||||
pub(super) fn glyph_info(&mut self, c: char) -> Option<GlyphInfo> {
|
||||
{
|
||||
if let Some(glyph_info) = self.glyph_info_cache.get(&c) {
|
||||
return Some(*glyph_info);
|
||||
@@ -424,7 +424,9 @@ impl Font<'_> {
|
||||
return *font_index_glyph_info;
|
||||
}
|
||||
|
||||
let font_index_glyph_info = self.glyph_info_no_cache_or_fallback(c);
|
||||
let font_index_glyph_info = self
|
||||
.cached_family
|
||||
.glyph_info_no_cache_or_fallback(c, self.fonts_by_id);
|
||||
let font_index_glyph_info =
|
||||
font_index_glyph_info.unwrap_or(self.cached_family.replacement_glyph);
|
||||
self.cached_family
|
||||
@@ -471,25 +473,6 @@ impl Font<'_> {
|
||||
self.row_height(font_size)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn glyph_info_no_cache_or_fallback(
|
||||
&mut self,
|
||||
c: char,
|
||||
) -> Option<(FontFaceKey, GlyphInfo)> {
|
||||
for font_key in &self.cached_family.fonts {
|
||||
let font_impl = self
|
||||
.fonts_by_id
|
||||
.get_mut(font_key)
|
||||
.expect("Nonexistent font ID");
|
||||
if let Some(glyph_info) = font_impl.glyph_info(c) {
|
||||
self.cached_family
|
||||
.glyph_info_cache
|
||||
.insert(c, (*font_key, glyph_info));
|
||||
return Some((*font_key, glyph_info));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Code points that will always be invisible (zero width).
|
||||
|
||||
@@ -434,7 +434,6 @@ impl CachedFamily {
|
||||
fn new(
|
||||
fonts: Vec<FontFaceKey>,
|
||||
fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontImpl>,
|
||||
atlas: &mut TextureAtlas,
|
||||
) -> Self {
|
||||
if fonts.is_empty() {
|
||||
return Self {
|
||||
@@ -455,15 +454,9 @@ impl CachedFamily {
|
||||
const PRIMARY_REPLACEMENT_CHAR: char = '◻'; // white medium square
|
||||
const FALLBACK_REPLACEMENT_CHAR: char = '?'; // fallback for the fallback
|
||||
|
||||
let mut font = Font {
|
||||
fonts_by_id,
|
||||
cached_family: &mut slf,
|
||||
atlas,
|
||||
};
|
||||
|
||||
let replacement_glyph = font
|
||||
.glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR)
|
||||
.or_else(|| font.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR))
|
||||
let replacement_glyph = slf
|
||||
.glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR, fonts_by_id)
|
||||
.or_else(|| slf.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR, fonts_by_id))
|
||||
.unwrap_or_else(|| {
|
||||
#[cfg(feature = "log")]
|
||||
log::warn!(
|
||||
@@ -475,6 +468,21 @@ impl CachedFamily {
|
||||
|
||||
slf
|
||||
}
|
||||
|
||||
pub(crate) fn glyph_info_no_cache_or_fallback(
|
||||
&mut self,
|
||||
c: char,
|
||||
fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontImpl>,
|
||||
) -> Option<(FontFaceKey, GlyphInfo)> {
|
||||
for font_key in &self.fonts {
|
||||
let font_impl = fonts_by_id.get_mut(font_key).expect("Nonexistent font ID");
|
||||
if let Some(glyph_info) = font_impl.glyph_info(c) {
|
||||
self.glyph_info_cache.insert(c, (*font_key, glyph_info));
|
||||
return Some((*font_key, glyph_info));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -497,21 +505,14 @@ impl Fonts {
|
||||
/// Create a new [`Fonts`] for text layout.
|
||||
/// This call is expensive, so only create one [`Fonts`] and then reuse it.
|
||||
///
|
||||
/// * `pixels_per_point`: how many physical pixels per logical "point".
|
||||
/// * `max_texture_side`: largest supported texture size (one side).
|
||||
pub fn new(
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
text_alpha_from_coverage: AlphaFromCoverage,
|
||||
definitions: FontDefinitions,
|
||||
) -> Self {
|
||||
Self {
|
||||
fonts: FontsImpl::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
text_alpha_from_coverage,
|
||||
definitions,
|
||||
),
|
||||
fonts: FontsImpl::new(max_texture_side, text_alpha_from_coverage, definitions),
|
||||
galley_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -525,30 +526,21 @@ impl Fonts {
|
||||
/// as well as notice when the font atlas is getting full, and handle that.
|
||||
pub fn begin_pass(
|
||||
&mut self,
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
text_alpha_from_coverage: AlphaFromCoverage,
|
||||
) {
|
||||
let pixels_per_point_changed = self.fonts.pixels_per_point != pixels_per_point;
|
||||
let max_texture_side_changed = self.fonts.max_texture_side != max_texture_side;
|
||||
let text_alpha_from_coverage_changed =
|
||||
self.fonts.atlas.text_alpha_from_coverage != text_alpha_from_coverage;
|
||||
let font_atlas_almost_full = self.fonts.atlas.fill_ratio() > 0.8;
|
||||
let needs_recreate = pixels_per_point_changed
|
||||
|| max_texture_side_changed
|
||||
|| text_alpha_from_coverage_changed
|
||||
|| font_atlas_almost_full;
|
||||
let needs_recreate =
|
||||
max_texture_side_changed || text_alpha_from_coverage_changed || font_atlas_almost_full;
|
||||
|
||||
if needs_recreate {
|
||||
let definitions = self.fonts.definitions.clone();
|
||||
|
||||
*self = Self {
|
||||
fonts: FontsImpl::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
text_alpha_from_coverage,
|
||||
definitions,
|
||||
),
|
||||
fonts: FontsImpl::new(max_texture_side, text_alpha_from_coverage, definitions),
|
||||
galley_cache: Default::default(),
|
||||
};
|
||||
}
|
||||
@@ -562,13 +554,13 @@ impl Fonts {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pixels_per_point(&self) -> f32 {
|
||||
self.fonts.pixels_per_point
|
||||
pub fn max_texture_side(&self) -> usize {
|
||||
self.fonts.max_texture_side
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_texture_side(&self) -> usize {
|
||||
self.fonts.max_texture_side
|
||||
pub fn definitions(&self) -> &FontDefinitions {
|
||||
&self.fonts.definitions
|
||||
}
|
||||
|
||||
/// The font atlas.
|
||||
@@ -589,29 +581,92 @@ impl Fonts {
|
||||
self.fonts.atlas.size()
|
||||
}
|
||||
|
||||
/// Width of this character in points.
|
||||
#[inline]
|
||||
pub fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
|
||||
self.fonts.glyph_width(font_id, c)
|
||||
}
|
||||
|
||||
/// Can we display this glyph?
|
||||
#[inline]
|
||||
pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
|
||||
self.fonts.has_glyph(font_id, c)
|
||||
self.fonts.font(&font_id.family).has_glyph(c)
|
||||
}
|
||||
|
||||
/// Can we display all the glyphs in this text?
|
||||
pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
|
||||
self.fonts.has_glyphs(font_id, s)
|
||||
self.fonts.font(&font_id.family).has_glyphs(s)
|
||||
}
|
||||
|
||||
pub fn num_galleys_in_cache(&self) -> usize {
|
||||
self.galley_cache.num_galleys_in_cache()
|
||||
}
|
||||
|
||||
/// How full is the font atlas?
|
||||
///
|
||||
/// This increases as new fonts and/or glyphs are used,
|
||||
/// but can also decrease in a call to [`Self::begin_pass`].
|
||||
pub fn font_atlas_fill_ratio(&self) -> f32 {
|
||||
self.fonts.atlas.fill_ratio()
|
||||
}
|
||||
|
||||
/// Returns a [`FontsView`] with the given `pixels_per_point` that can be used to do text layout.
|
||||
pub fn with_pixels_per_point(&mut self, pixels_per_point: f32) -> FontsView<'_> {
|
||||
FontsView {
|
||||
fonts: &mut self.fonts,
|
||||
galley_cache: &mut self.galley_cache,
|
||||
pixels_per_point,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// The context's collection of fonts, with this context's `pixels_per_point`. This is what you use to do text layout.
|
||||
pub struct FontsView<'a> {
|
||||
pub fonts: &'a mut FontsImpl,
|
||||
galley_cache: &'a mut GalleyCache,
|
||||
pixels_per_point: f32,
|
||||
}
|
||||
|
||||
impl FontsView<'_> {
|
||||
#[inline]
|
||||
pub fn max_texture_side(&self) -> usize {
|
||||
self.fonts.max_texture_side
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn definitions(&self) -> &FontDefinitions {
|
||||
&self.fonts.definitions
|
||||
}
|
||||
|
||||
/// The full font atlas image.
|
||||
#[inline]
|
||||
pub fn image(&self) -> crate::ColorImage {
|
||||
self.fonts.atlas.image().clone()
|
||||
}
|
||||
|
||||
/// Current size of the font image.
|
||||
/// Pass this to [`crate::Tessellator`].
|
||||
pub fn font_image_size(&self) -> [usize; 2] {
|
||||
self.fonts.atlas.size()
|
||||
}
|
||||
|
||||
/// Width of this character in points.
|
||||
pub fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
|
||||
self.fonts
|
||||
.font(&font_id.family)
|
||||
.glyph_width(c, font_id.size)
|
||||
}
|
||||
|
||||
/// Can we display this glyph?
|
||||
pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
|
||||
self.fonts.font(&font_id.family).has_glyph(c)
|
||||
}
|
||||
|
||||
/// Can we display all the glyphs in this text?
|
||||
pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
|
||||
self.fonts.font(&font_id.family).has_glyphs(s)
|
||||
}
|
||||
|
||||
/// Height of one row of text in points.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
#[inline]
|
||||
pub fn row_height(&mut self, font_id: &FontId) -> f32 {
|
||||
self.fonts.row_height(font_id)
|
||||
self.fonts.font(&font_id.family).row_height(font_id.size)
|
||||
}
|
||||
|
||||
/// List of all known font families.
|
||||
@@ -629,8 +684,12 @@ impl Fonts {
|
||||
#[inline]
|
||||
pub fn layout_job(&mut self, job: LayoutJob) -> Arc<Galley> {
|
||||
let allow_split_paragraphs = true; // Optimization for editing text with many paragraphs.
|
||||
self.galley_cache
|
||||
.layout(&mut self.fonts, job, allow_split_paragraphs)
|
||||
self.galley_cache.layout(
|
||||
self.fonts,
|
||||
job,
|
||||
self.pixels_per_point,
|
||||
allow_split_paragraphs,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn num_galleys_in_cache(&self) -> usize {
|
||||
@@ -687,13 +746,10 @@ impl Fonts {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// The collection of fonts used by `epaint`.
|
||||
///
|
||||
/// Required in order to paint text.
|
||||
pub struct FontsImpl {
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
definitions: FontDefinitions,
|
||||
atlas: TextureAtlas,
|
||||
@@ -706,16 +762,10 @@ impl FontsImpl {
|
||||
/// Create a new [`FontsImpl`] for text layout.
|
||||
/// This call is expensive, so only create one [`FontsImpl`] and then reuse it.
|
||||
pub fn new(
|
||||
pixels_per_point: f32,
|
||||
max_texture_side: usize,
|
||||
text_alpha_from_coverage: AlphaFromCoverage,
|
||||
definitions: FontDefinitions,
|
||||
) -> Self {
|
||||
assert!(
|
||||
0.0 < pixels_per_point && pixels_per_point < 100.0,
|
||||
"pixels_per_point out of range: {pixels_per_point}"
|
||||
);
|
||||
|
||||
let texture_width = max_texture_side.at_most(16 * 1024);
|
||||
let initial_height = 32; // Keep initial font atlas small, so it is fast to upload to GPU. This will expand as needed anyways.
|
||||
let atlas = TextureAtlas::new([texture_width, initial_height], text_alpha_from_coverage);
|
||||
@@ -732,7 +782,6 @@ impl FontsImpl {
|
||||
}
|
||||
|
||||
Self {
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
definitions,
|
||||
atlas,
|
||||
@@ -742,16 +791,6 @@ impl FontsImpl {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn pixels_per_point(&self) -> f32 {
|
||||
self.pixels_per_point
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn definitions(&self) -> &FontDefinitions {
|
||||
&self.definitions
|
||||
}
|
||||
|
||||
/// Get the right font implementation from [`FontFamily`].
|
||||
pub fn font(&mut self, family: &FontFamily) -> Font<'_> {
|
||||
let cached_family = self.family_cache.entry(family.clone()).or_insert_with(|| {
|
||||
@@ -769,7 +808,7 @@ impl FontsImpl {
|
||||
})
|
||||
.collect();
|
||||
|
||||
CachedFamily::new(fonts, &mut self.fonts_by_id, &mut self.atlas)
|
||||
CachedFamily::new(fonts, &mut self.fonts_by_id)
|
||||
});
|
||||
Font {
|
||||
fonts_by_id: &mut self.fonts_by_id,
|
||||
@@ -777,28 +816,6 @@ impl FontsImpl {
|
||||
atlas: &mut self.atlas,
|
||||
}
|
||||
}
|
||||
|
||||
/// Width of this character in points.
|
||||
fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
|
||||
self.font(&font_id.family).glyph_width(c, font_id.size)
|
||||
}
|
||||
|
||||
/// Can we display this glyph?
|
||||
pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
|
||||
self.font(&font_id.family).has_glyph(c)
|
||||
}
|
||||
|
||||
/// Can we display all the glyphs in this text?
|
||||
pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
|
||||
self.font(&font_id.family).has_glyphs(s)
|
||||
}
|
||||
|
||||
/// Height of one row of text in points.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
fn row_height(&mut self, font_id: &FontId) -> f32 {
|
||||
self.font(&font_id.family).row_height(font_id.size)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -827,6 +844,7 @@ impl GalleyCache {
|
||||
&mut self,
|
||||
fonts: &mut FontsImpl,
|
||||
mut job: LayoutJob,
|
||||
pixels_per_point: f32,
|
||||
allow_split_paragraphs: bool,
|
||||
) -> (u64, Arc<Galley>) {
|
||||
if job.wrap.max_width.is_finite() {
|
||||
@@ -882,14 +900,13 @@ impl GalleyCache {
|
||||
let job = Arc::new(job);
|
||||
if allow_split_paragraphs && should_cache_each_paragraph_individually(&job) {
|
||||
let (child_galleys, child_hashes) =
|
||||
self.layout_each_paragraph_individually(fonts, &job);
|
||||
self.layout_each_paragraph_individually(fonts, &job, pixels_per_point);
|
||||
debug_assert_eq!(
|
||||
child_hashes.len(),
|
||||
child_galleys.len(),
|
||||
"Bug in `layout_each_paragraph_individuallly`"
|
||||
);
|
||||
let galley =
|
||||
Arc::new(Galley::concat(job, &child_galleys, fonts.pixels_per_point));
|
||||
let galley = Arc::new(Galley::concat(job, &child_galleys, pixels_per_point));
|
||||
|
||||
self.cache.insert(
|
||||
hash,
|
||||
@@ -901,7 +918,7 @@ impl GalleyCache {
|
||||
);
|
||||
galley
|
||||
} else {
|
||||
let galley = super::layout(fonts, job);
|
||||
let galley = super::layout(fonts, job, pixels_per_point);
|
||||
let galley = Arc::new(galley);
|
||||
entry.insert(CachedGalley {
|
||||
last_used: self.generation,
|
||||
@@ -920,9 +937,11 @@ impl GalleyCache {
|
||||
&mut self,
|
||||
fonts: &mut FontsImpl,
|
||||
job: LayoutJob,
|
||||
pixels_per_point: f32,
|
||||
allow_split_paragraphs: bool,
|
||||
) -> Arc<Galley> {
|
||||
self.layout_internal(fonts, job, allow_split_paragraphs).1
|
||||
self.layout_internal(fonts, job, pixels_per_point, allow_split_paragraphs)
|
||||
.1
|
||||
}
|
||||
|
||||
/// Split on `\n` and lay out (and cache) each paragraph individually.
|
||||
@@ -930,6 +949,7 @@ impl GalleyCache {
|
||||
&mut self,
|
||||
fonts: &mut FontsImpl,
|
||||
job: &LayoutJob,
|
||||
pixels_per_point: f32,
|
||||
) -> (Vec<Arc<Galley>>, Vec<u64>) {
|
||||
profiling::function_scope!();
|
||||
|
||||
@@ -1009,7 +1029,8 @@ impl GalleyCache {
|
||||
}
|
||||
|
||||
// TODO(emilk): we could lay out each paragraph in parallel to get a nice speedup on multicore machines.
|
||||
let (hash, galley) = self.layout_internal(fonts, paragraph_job, false);
|
||||
let (hash, galley) =
|
||||
self.layout_internal(fonts, paragraph_job, pixels_per_point, false);
|
||||
child_hashes.push(hash);
|
||||
|
||||
// This will prevent us from invalidating cache entries unnecessarily:
|
||||
@@ -1164,7 +1185,6 @@ mod tests {
|
||||
for pixels_per_point in [1.0, 2.0_f32.sqrt(), 2.0] {
|
||||
let max_texture_side = 4096;
|
||||
let mut fonts = FontsImpl::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
@@ -1176,9 +1196,11 @@ mod tests {
|
||||
job.halign = halign;
|
||||
job.justify = justify;
|
||||
|
||||
let whole = GalleyCache::default().layout(&mut fonts, job.clone(), false);
|
||||
let whole =
|
||||
GalleyCache::default().layout(&mut fonts, job.clone(), 1., false);
|
||||
|
||||
let split = GalleyCache::default().layout(&mut fonts, job.clone(), true);
|
||||
let split =
|
||||
GalleyCache::default().layout(&mut fonts, job.clone(), 1., true);
|
||||
|
||||
for (i, row) in whole.rows.iter().enumerate() {
|
||||
println!(
|
||||
@@ -1217,7 +1239,6 @@ mod tests {
|
||||
|
||||
for pixels_per_point in pixels_per_point {
|
||||
let mut fonts = FontsImpl::new(
|
||||
pixels_per_point,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
@@ -1230,12 +1251,13 @@ mod tests {
|
||||
|
||||
job.round_output_to_gui = round_output_to_gui;
|
||||
|
||||
let galley_wrapped = layout(&mut fonts, job.clone().into());
|
||||
let galley_wrapped =
|
||||
layout(&mut fonts, job.clone().into(), pixels_per_point);
|
||||
|
||||
job.wrap = TextWrapping::no_max_width();
|
||||
|
||||
let text = job.text.clone();
|
||||
let galley_unwrapped = layout(&mut fonts, job.into());
|
||||
let galley_unwrapped = layout(&mut fonts, job.into(), pixels_per_point);
|
||||
|
||||
let intrinsic_size = galley_wrapped.intrinsic_size();
|
||||
let unwrapped_size = galley_unwrapped.size();
|
||||
|
||||
@@ -12,7 +12,7 @@ pub const TAB_SIZE: usize = 4;
|
||||
pub use {
|
||||
fonts::{
|
||||
FontData, FontDefinitions, FontFamily, FontId, FontInsert, FontPriority, FontTweak, Fonts,
|
||||
FontsImpl, InsertFontFamily,
|
||||
FontsImpl, FontsView, InsertFontFamily,
|
||||
},
|
||||
text_layout::*,
|
||||
text_layout_types::*,
|
||||
|
||||
@@ -68,7 +68,7 @@ impl Paragraph {
|
||||
///
|
||||
/// In most cases you should use [`crate::Fonts::layout_job`] instead
|
||||
/// since that memoizes the input, making subsequent layouting of the same text much faster.
|
||||
pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
|
||||
pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>, pixels_per_point: f32) -> Galley {
|
||||
profiling::function_scope!();
|
||||
|
||||
if job.wrap.max_rows == 0 {
|
||||
@@ -80,7 +80,7 @@ pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
|
||||
mesh_bounds: Rect::NOTHING,
|
||||
num_vertices: 0,
|
||||
num_indices: 0,
|
||||
pixels_per_point: fonts.pixels_per_point(),
|
||||
pixels_per_point,
|
||||
elided: true,
|
||||
intrinsic_size: Vec2::ZERO,
|
||||
};
|
||||
@@ -90,10 +90,17 @@ pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
|
||||
|
||||
let mut paragraphs = vec![Paragraph::from_section_index(0)];
|
||||
for (section_index, section) in job.sections.iter().enumerate() {
|
||||
layout_section(fonts, &job, section_index as u32, section, &mut paragraphs);
|
||||
layout_section(
|
||||
fonts,
|
||||
&job,
|
||||
pixels_per_point,
|
||||
section_index as u32,
|
||||
section,
|
||||
&mut paragraphs,
|
||||
);
|
||||
}
|
||||
|
||||
let point_scale = PointScale::new(fonts.pixels_per_point());
|
||||
let point_scale = PointScale::new(pixels_per_point);
|
||||
|
||||
let intrinsic_size = calculate_intrinsic_size(point_scale, &job, ¶graphs);
|
||||
|
||||
@@ -102,7 +109,7 @@ pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
|
||||
if elided {
|
||||
if let Some(last_placed) = rows.last_mut() {
|
||||
let last_row = Arc::make_mut(&mut last_placed.row);
|
||||
replace_last_glyph_with_overflow_character(fonts, &job, last_row);
|
||||
replace_last_glyph_with_overflow_character(fonts, &job, last_row, pixels_per_point);
|
||||
if let Some(last) = last_row.glyphs.last() {
|
||||
last_row.size.x = last.max_x();
|
||||
}
|
||||
@@ -134,6 +141,7 @@ pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
|
||||
fn layout_section(
|
||||
fonts: &mut FontsImpl,
|
||||
job: &LayoutJob,
|
||||
pixels_per_point: f32,
|
||||
section_index: u32,
|
||||
section: &LayoutSection,
|
||||
out_paragraphs: &mut Vec<Paragraph>,
|
||||
@@ -143,7 +151,6 @@ fn layout_section(
|
||||
byte_range,
|
||||
format,
|
||||
} = section;
|
||||
let pixels_per_point = fonts.pixels_per_point();
|
||||
let mut font = fonts.font(&format.font_id.family);
|
||||
let font_size = format.font_id.size;
|
||||
let line_height = section
|
||||
@@ -407,6 +414,7 @@ fn replace_last_glyph_with_overflow_character(
|
||||
fonts: &mut FontsImpl,
|
||||
job: &LayoutJob,
|
||||
row: &mut Row,
|
||||
pixels_per_point: f32,
|
||||
) {
|
||||
fn row_width(row: &Row) -> f32 {
|
||||
if let (Some(first), Some(last)) = (row.glyphs.first(), row.glyphs.last()) {
|
||||
@@ -427,8 +435,6 @@ fn replace_last_glyph_with_overflow_character(
|
||||
return;
|
||||
};
|
||||
|
||||
let pixels_per_point = fonts.pixels_per_point();
|
||||
|
||||
// We always try to just append the character first:
|
||||
if let Some(last_glyph) = row.glyphs.last() {
|
||||
let section_index = last_glyph.section_index;
|
||||
@@ -514,7 +520,6 @@ fn replace_last_glyph_with_overflow_character(
|
||||
|
||||
let section = &job.sections[last_glyph.section_index as usize];
|
||||
let extra_letter_spacing = section.format.extra_letter_spacing;
|
||||
let pixels_per_point = fonts.pixels_per_point();
|
||||
let mut font = fonts.font(§ion.format.font_id.family);
|
||||
let font_size = section.format.font_id.size;
|
||||
|
||||
@@ -568,7 +573,6 @@ fn replace_last_glyph_with_overflow_character(
|
||||
let Some(section) = &job.sections.get(last_glyph.section_index as usize) else {
|
||||
return;
|
||||
};
|
||||
let pixels_per_point = fonts.pixels_per_point();
|
||||
let mut font = fonts.font(§ion.format.font_id.family);
|
||||
let font_size = section.format.font_id.size;
|
||||
// Just replace and be done with it.
|
||||
@@ -1119,14 +1123,13 @@ mod tests {
|
||||
#[test]
|
||||
fn test_zero_max_width() {
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
let mut layout_job = LayoutJob::single_section("W".into(), TextFormat::default());
|
||||
layout_job.wrap.max_width = 0.0;
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
let galley = layout(&mut fonts, layout_job.into(), 1.0);
|
||||
assert_eq!(galley.rows.len(), 1);
|
||||
}
|
||||
|
||||
@@ -1135,7 +1138,6 @@ mod tests {
|
||||
// No matter where we wrap, we should be appending the newline character.
|
||||
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
@@ -1154,7 +1156,7 @@ mod tests {
|
||||
layout_job.wrap.max_rows = 1;
|
||||
layout_job.wrap.break_anywhere = break_anywhere;
|
||||
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
let galley = layout(&mut fonts, layout_job.into(), 1.0);
|
||||
|
||||
assert!(galley.elided);
|
||||
assert_eq!(galley.rows.len(), 1);
|
||||
@@ -1173,7 +1175,7 @@ mod tests {
|
||||
layout_job.wrap.max_rows = 1;
|
||||
layout_job.wrap.break_anywhere = false;
|
||||
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
let galley = layout(&mut fonts, layout_job.into(), 1.0);
|
||||
|
||||
assert!(galley.elided);
|
||||
assert_eq!(galley.rows.len(), 1);
|
||||
@@ -1185,7 +1187,6 @@ mod tests {
|
||||
#[test]
|
||||
fn test_cjk() {
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
@@ -1195,7 +1196,7 @@ mod tests {
|
||||
TextFormat::default(),
|
||||
);
|
||||
layout_job.wrap.max_width = 90.0;
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
let galley = layout(&mut fonts, layout_job.into(), 1.0);
|
||||
assert_eq!(
|
||||
galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),
|
||||
vec!["日本語と", "Englishの混在", "した文章"]
|
||||
@@ -1205,7 +1206,6 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pre_cjk() {
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
@@ -1215,7 +1215,7 @@ mod tests {
|
||||
TextFormat::default(),
|
||||
);
|
||||
layout_job.wrap.max_width = 110.0;
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
let galley = layout(&mut fonts, layout_job.into(), 1.0);
|
||||
assert_eq!(
|
||||
galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),
|
||||
vec!["日本語とEnglish", "の混在した文章"]
|
||||
@@ -1225,7 +1225,6 @@ mod tests {
|
||||
#[test]
|
||||
fn test_truncate_width() {
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
@@ -1235,7 +1234,7 @@ mod tests {
|
||||
layout_job.wrap.max_width = f32::INFINITY;
|
||||
layout_job.wrap.max_rows = 1;
|
||||
layout_job.round_output_to_gui = false;
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
let galley = layout(&mut fonts, layout_job.into(), 1.0);
|
||||
assert!(galley.elided);
|
||||
assert_eq!(
|
||||
galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),
|
||||
@@ -1249,18 +1248,17 @@ mod tests {
|
||||
#[test]
|
||||
fn test_empty_row() {
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
|
||||
let font_id = FontId::default();
|
||||
let font_height = fonts.font(&font_id).row_height();
|
||||
let font_height = fonts.font(&font_id.family).row_height(font_id.size);
|
||||
|
||||
let job = LayoutJob::simple(String::new(), font_id, Color32::WHITE, f32::INFINITY);
|
||||
|
||||
let galley = layout(&mut fonts, job.into());
|
||||
let galley = layout(&mut fonts, job.into(), 1.0);
|
||||
|
||||
assert_eq!(galley.rows.len(), 1, "Expected one row");
|
||||
assert_eq!(
|
||||
@@ -1283,18 +1281,17 @@ mod tests {
|
||||
#[test]
|
||||
fn test_end_with_newline() {
|
||||
let mut fonts = FontsImpl::new(
|
||||
1.0,
|
||||
1024,
|
||||
AlphaFromCoverage::default(),
|
||||
FontDefinitions::default(),
|
||||
);
|
||||
|
||||
let font_id = FontId::default();
|
||||
let font_height = fonts.font(&font_id).row_height();
|
||||
let font_height = fonts.font(&font_id.family).row_height(font_id.size);
|
||||
|
||||
let job = LayoutJob::simple("Hi!\n".to_owned(), font_id, Color32::WHITE, f32::INFINITY);
|
||||
|
||||
let galley = layout(&mut fonts, job.into());
|
||||
let galley = layout(&mut fonts, job.into(), 1.0);
|
||||
|
||||
assert_eq!(galley.rows.len(), 2, "Expected two rows");
|
||||
assert_eq!(
|
||||
|
||||
@@ -8,7 +8,7 @@ use super::{
|
||||
cursor::{CCursor, LayoutCursor},
|
||||
font::UvRect,
|
||||
};
|
||||
use crate::{Color32, FontId, Mesh, Stroke};
|
||||
use crate::{Color32, FontId, Mesh, Stroke, text::FontsView};
|
||||
use emath::{Align, GuiRounding as _, NumExt as _, OrderedFloat, Pos2, Rect, Vec2, pos2, vec2};
|
||||
|
||||
/// Describes the task of laying out text.
|
||||
@@ -184,7 +184,7 @@ impl LayoutJob {
|
||||
/// The height of the tallest font used in the job.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
pub fn font_height(&self, fonts: &mut crate::Fonts) -> f32 {
|
||||
pub fn font_height(&self, fonts: &mut FontsView<'_>) -> f32 {
|
||||
let mut max_height = 0.0_f32;
|
||||
for section in &self.sections {
|
||||
max_height = max_height.max(fonts.row_height(§ion.format.font_id));
|
||||
|
||||
Reference in New Issue
Block a user