mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 13:50:04 -04:00
Stop wrapping FontsImpl in an Arc<Mutex<_>>
We never need to clone it, and none of its methods took `self` as mutable, meaning it wasn't making use of the semantic difference between the two. This API is about to get reworked, and simplifying it is a first step.
This commit is contained in:
@@ -60,7 +60,7 @@ pub trait AtomExt<'a> {
|
|||||||
{
|
{
|
||||||
let font_selection = FontSelection::default();
|
let font_selection = FontSelection::default();
|
||||||
let font_id = font_selection.resolve(ui.style());
|
let font_id = font_selection.resolve(ui.style());
|
||||||
let height = ui.fonts(|f| f.row_height(&font_id));
|
let height = ui.fonts_mut(|f| f.row_height(&font_id));
|
||||||
self.atom_max_height(height)
|
self.atom_max_height(height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ impl Window<'_> {
|
|||||||
let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {
|
let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {
|
||||||
let style = ctx.style();
|
let style = ctx.style();
|
||||||
let title_bar_inner_height = ctx
|
let title_bar_inner_height = ctx
|
||||||
.fonts(|fonts| title.font_height(fonts, &style))
|
.fonts_mut(|fonts| title.font_height(fonts, &style))
|
||||||
.at_least(style.spacing.interact_size.y);
|
.at_least(style.spacing.interact_size.y);
|
||||||
let title_bar_inner_height = title_bar_inner_height + window_frame.inner_margin.sum().y;
|
let title_bar_inner_height = title_bar_inner_height + window_frame.inner_margin.sum().y;
|
||||||
let half_height = (title_bar_inner_height / 2.0).round() as _;
|
let half_height = (title_bar_inner_height / 2.0).round() as _;
|
||||||
|
|||||||
@@ -641,7 +641,7 @@ impl ContextImpl {
|
|||||||
// Preload the most common characters for the most common fonts.
|
// Preload the most common characters for the most common fonts.
|
||||||
// This is not very important to do, but may save a few GPU operations.
|
// This is not very important to do, but may save a few GPU operations.
|
||||||
for font_id in self.memory.options.style().text_styles.values() {
|
for font_id in self.memory.options.style().text_styles.values() {
|
||||||
fonts.lock().fonts.font(font_id).preload_common_characters();
|
fonts.fonts.font(font_id).preload_common_characters();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1060,6 +1060,22 @@ impl Context {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read-write access to [`Fonts`].
|
||||||
|
///
|
||||||
|
/// 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 {
|
||||||
|
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()"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Read-only access to [`Options`].
|
/// Read-only access to [`Options`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn options<R>(&self, reader: impl FnOnce(&Options) -> R) -> R {
|
pub fn options<R>(&self, reader: impl FnOnce(&Options) -> R) -> R {
|
||||||
@@ -1568,9 +1584,8 @@ impl Context {
|
|||||||
} = ModifierNames::SYMBOLS;
|
} = ModifierNames::SYMBOLS;
|
||||||
|
|
||||||
let font_id = TextStyle::Body.resolve(&self.style());
|
let font_id = TextStyle::Body.resolve(&self.style());
|
||||||
self.fonts(|f| {
|
self.fonts_mut(|f| {
|
||||||
let mut lock = f.lock();
|
let font = f.fonts.font(&font_id);
|
||||||
let font = lock.fonts.font(&font_id);
|
|
||||||
font.has_glyphs(alt)
|
font.has_glyphs(alt)
|
||||||
&& font.has_glyphs(ctrl)
|
&& font.has_glyphs(ctrl)
|
||||||
&& font.has_glyphs(shift)
|
&& font.has_glyphs(shift)
|
||||||
@@ -1927,7 +1942,7 @@ impl Context {
|
|||||||
self.read(|ctx| {
|
self.read(|ctx| {
|
||||||
if let Some(current_fonts) = ctx.fonts.get(&pixels_per_point.into()) {
|
if let Some(current_fonts) = ctx.fonts.get(&pixels_per_point.into()) {
|
||||||
// NOTE: this comparison is expensive since it checks TTF data for equality
|
// NOTE: this comparison is expensive since it checks TTF data for equality
|
||||||
if current_fonts.lock().fonts.definitions() == &font_definitions {
|
if current_fonts.fonts.definitions() == &font_definitions {
|
||||||
update_fonts = false; // no need to update
|
update_fonts = false; // no need to update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1955,7 +1970,6 @@ impl Context {
|
|||||||
self.read(|ctx| {
|
self.read(|ctx| {
|
||||||
if let Some(current_fonts) = ctx.fonts.get(&pixels_per_point.into()) {
|
if let Some(current_fonts) = ctx.fonts.get(&pixels_per_point.into()) {
|
||||||
if current_fonts
|
if current_fonts
|
||||||
.lock()
|
|
||||||
.fonts
|
.fonts
|
||||||
.definitions()
|
.definitions()
|
||||||
.font_data
|
.font_data
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ impl State {
|
|||||||
{
|
{
|
||||||
// Paint location to left of `pos`:
|
// Paint location to left of `pos`:
|
||||||
let location_galley =
|
let location_galley =
|
||||||
ctx.fonts(|f| f.layout(location, font_id.clone(), color, f32::INFINITY));
|
ctx.fonts_mut(|f| f.layout(location, font_id.clone(), color, f32::INFINITY));
|
||||||
let location_rect =
|
let location_rect =
|
||||||
Align2::RIGHT_TOP.anchor_size(pos - 4.0 * Vec2::X, location_galley.size());
|
Align2::RIGHT_TOP.anchor_size(pos - 4.0 * Vec2::X, location_galley.size());
|
||||||
painter.galley(location_rect.min, location_galley, color);
|
painter.galley(location_rect.min, location_galley, color);
|
||||||
|
|||||||
@@ -149,6 +149,14 @@ impl Painter {
|
|||||||
self.ctx.fonts(reader)
|
self.ctx.fonts(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read-write access to the shared [`Fonts`].
|
||||||
|
///
|
||||||
|
/// See [`Context`] documentation for how locks work.
|
||||||
|
#[inline]
|
||||||
|
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut Fonts) -> R) -> R {
|
||||||
|
self.ctx.fonts_mut(reader)
|
||||||
|
}
|
||||||
|
|
||||||
/// Where we paint
|
/// Where we paint
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn layer_id(&self) -> LayerId {
|
pub fn layer_id(&self) -> LayerId {
|
||||||
@@ -525,7 +533,7 @@ impl Painter {
|
|||||||
color: crate::Color32,
|
color: crate::Color32,
|
||||||
wrap_width: f32,
|
wrap_width: f32,
|
||||||
) -> Arc<Galley> {
|
) -> Arc<Galley> {
|
||||||
self.fonts(|f| f.layout(text, font_id, color, wrap_width))
|
self.fonts_mut(|f| f.layout(text, font_id, color, wrap_width))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Will line break at `\n`.
|
/// Will line break at `\n`.
|
||||||
@@ -539,7 +547,7 @@ impl Painter {
|
|||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
color: crate::Color32,
|
color: crate::Color32,
|
||||||
) -> Arc<Galley> {
|
) -> Arc<Galley> {
|
||||||
self.fonts(|f| f.layout(text, font_id, color, f32::INFINITY))
|
self.fonts_mut(|f| f.layout(text, font_id, color, f32::INFINITY))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lay out this text layut job in a galley.
|
/// Lay out this text layut job in a galley.
|
||||||
@@ -548,7 +556,7 @@ impl Painter {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn layout_job(&self, layout_job: LayoutJob) -> Arc<Galley> {
|
pub fn layout_job(&self, layout_job: LayoutJob) -> Arc<Galley> {
|
||||||
self.fonts(|f| f.layout_job(layout_job))
|
self.fonts_mut(|f| f.layout_job(layout_job))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paint text that has already been laid out in a [`Galley`].
|
/// Paint text that has already been laid out in a [`Galley`].
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ impl Ui {
|
|||||||
///
|
///
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
pub fn text_style_height(&self, style: &TextStyle) -> f32 {
|
pub fn text_style_height(&self, style: &TextStyle) -> f32 {
|
||||||
self.fonts(|f| f.row_height(&style.resolve(self.style())))
|
self.fonts_mut(|f| f.row_height(&style.resolve(self.style())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Screen-space rectangle for clipping what we paint in this ui.
|
/// Screen-space rectangle for clipping what we paint in this ui.
|
||||||
@@ -852,6 +852,12 @@ impl Ui {
|
|||||||
pub fn fonts<R>(&self, reader: impl FnOnce(&Fonts) -> R) -> R {
|
pub fn fonts<R>(&self, reader: impl FnOnce(&Fonts) -> R) -> R {
|
||||||
self.ctx().fonts(reader)
|
self.ctx().fonts(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read-write access to [`Fonts`].
|
||||||
|
#[inline]
|
||||||
|
pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut Fonts) -> R) -> R {
|
||||||
|
self.ctx().fonts_mut(reader)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ impl RichText {
|
|||||||
/// Read the font height of the selected text style.
|
/// Read the font height of the selected text style.
|
||||||
///
|
///
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
pub fn font_height(&self, fonts: &epaint::Fonts, style: &Style) -> f32 {
|
pub fn font_height(&self, fonts: &mut epaint::Fonts, style: &Style) -> f32 {
|
||||||
let mut font_id = self.text_style.as_ref().map_or_else(
|
let mut font_id = self.text_style.as_ref().map_or_else(
|
||||||
|| FontSelection::Default.resolve(style),
|
|| FontSelection::Default.resolve(style),
|
||||||
|text_style| text_style.resolve(style),
|
|text_style| text_style.resolve(style),
|
||||||
@@ -676,7 +676,7 @@ impl WidgetText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
pub(crate) fn font_height(&self, fonts: &epaint::Fonts, style: &Style) -> f32 {
|
pub(crate) fn font_height(&self, fonts: &mut epaint::Fonts, style: &Style) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Self::Text(_) => fonts.row_height(&FontSelection::Default.resolve(style)),
|
Self::Text(_) => fonts.row_height(&FontSelection::Default.resolve(style)),
|
||||||
Self::RichText(text) => text.font_height(fonts, style),
|
Self::RichText(text) => text.font_height(fonts, style),
|
||||||
@@ -762,7 +762,7 @@ impl WidgetText {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
layout_job.wrap = text_wrapping;
|
layout_job.wrap = text_wrapping;
|
||||||
ctx.fonts(|f| f.layout_job(layout_job))
|
ctx.fonts_mut(|f| f.layout_job(layout_job))
|
||||||
}
|
}
|
||||||
Self::RichText(text) => {
|
Self::RichText(text) => {
|
||||||
let mut layout_job = Arc::unwrap_or_clone(text).into_layout_job(
|
let mut layout_job = Arc::unwrap_or_clone(text).into_layout_job(
|
||||||
@@ -771,12 +771,12 @@ impl WidgetText {
|
|||||||
default_valign,
|
default_valign,
|
||||||
);
|
);
|
||||||
layout_job.wrap = text_wrapping;
|
layout_job.wrap = text_wrapping;
|
||||||
ctx.fonts(|f| f.layout_job(layout_job))
|
ctx.fonts_mut(|f| f.layout_job(layout_job))
|
||||||
}
|
}
|
||||||
Self::LayoutJob(job) => {
|
Self::LayoutJob(job) => {
|
||||||
let mut job = Arc::unwrap_or_clone(job);
|
let mut job = Arc::unwrap_or_clone(job);
|
||||||
job.wrap = text_wrapping;
|
job.wrap = text_wrapping;
|
||||||
ctx.fonts(|f| f.layout_job(job))
|
ctx.fonts_mut(|f| f.layout_job(job))
|
||||||
}
|
}
|
||||||
Self::Galley(galley) => galley,
|
Self::Galley(galley) => galley,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ impl Label {
|
|||||||
if let Some(first_section) = layout_job.sections.first_mut() {
|
if let Some(first_section) = layout_job.sections.first_mut() {
|
||||||
first_section.leading_space = first_row_indentation;
|
first_section.leading_space = first_row_indentation;
|
||||||
}
|
}
|
||||||
let galley = ui.fonts(|fonts| fonts.layout_job(layout_job));
|
let galley = ui.fonts_mut(|fonts| fonts.layout_job(layout_job));
|
||||||
|
|
||||||
let pos = pos2(ui.max_rect().left(), ui.cursor().top());
|
let pos = pos2(ui.max_rect().left(), ui.cursor().top());
|
||||||
assert!(!galley.rows.is_empty(), "Galleys are never empty");
|
assert!(!galley.rows.is_empty(), "Galleys are never empty");
|
||||||
@@ -252,7 +252,7 @@ impl Label {
|
|||||||
layout_job.justify = ui.layout().horizontal_justify();
|
layout_job.justify = ui.layout().horizontal_justify();
|
||||||
}
|
}
|
||||||
|
|
||||||
let galley = ui.fonts(|fonts| fonts.layout_job(layout_job));
|
let galley = ui.fonts_mut(|fonts| fonts.layout_job(layout_job));
|
||||||
let (rect, mut response) = ui.allocate_exact_size(galley.size(), sense);
|
let (rect, mut response) = ui.allocate_exact_size(galley.size(), sense);
|
||||||
response.intrinsic_size = Some(galley.intrinsic_size());
|
response.intrinsic_size = Some(galley.intrinsic_size());
|
||||||
let galley_pos = match galley.job.halign {
|
let galley_pos = match galley.job.halign {
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ impl TextEdit<'_> {
|
|||||||
let hint_text_str = hint_text.text().to_owned();
|
let hint_text_str = hint_text.text().to_owned();
|
||||||
|
|
||||||
let font_id = font_selection.resolve(ui.style());
|
let font_id = font_selection.resolve(ui.style());
|
||||||
let row_height = ui.fonts(|f| f.row_height(&font_id));
|
let row_height = ui.fonts_mut(|f| f.row_height(&font_id));
|
||||||
const MIN_WIDTH: f32 = 24.0; // Never make a [`TextEdit`] more narrow than this.
|
const MIN_WIDTH: f32 = 24.0; // Never make a [`TextEdit`] more narrow than this.
|
||||||
let available_width = (ui.available_width() - margin.sum().x).at_least(MIN_WIDTH);
|
let available_width = (ui.available_width() - margin.sum().x).at_least(MIN_WIDTH);
|
||||||
let desired_width = desired_width.unwrap_or_else(|| ui.spacing().text_edit_width);
|
let desired_width = desired_width.unwrap_or_else(|| ui.spacing().text_edit_width);
|
||||||
@@ -522,7 +522,7 @@ impl TextEdit<'_> {
|
|||||||
} else {
|
} else {
|
||||||
LayoutJob::simple_singleline(text, font_id_clone.clone(), text_color)
|
LayoutJob::simple_singleline(text, font_id_clone.clone(), text_color)
|
||||||
};
|
};
|
||||||
ui.fonts(|f| f.layout_job(layout_job))
|
ui.fonts_mut(|f| f.layout_job(layout_job))
|
||||||
};
|
};
|
||||||
|
|
||||||
let layouter = layouter.unwrap_or(&mut default_layouter);
|
let layouter = layouter.unwrap_or(&mut default_layouter);
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ impl ColoredText {
|
|||||||
pub fn ui(&self, ui: &mut egui::Ui) {
|
pub fn ui(&self, ui: &mut egui::Ui) {
|
||||||
let mut job = self.0.clone();
|
let mut job = self.0.clone();
|
||||||
job.wrap.max_width = ui.available_width();
|
job.wrap.max_width = ui.available_width();
|
||||||
let galley = ui.fonts(|f| f.layout_job(job));
|
let galley = ui.fonts_mut(|f| f.layout_job(job));
|
||||||
ui.add(egui::Label::new(galley).selectable(true));
|
ui.add(egui::Label::new(galley).selectable(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ impl FrameHistory {
|
|||||||
));
|
));
|
||||||
let cpu_usage = to_screen.inverse().transform_pos(pointer_pos).y;
|
let cpu_usage = to_screen.inverse().transform_pos(pointer_pos).y;
|
||||||
let text = format!("{:.1} ms", 1e3 * cpu_usage);
|
let text = format!("{:.1} ms", 1e3 * cpu_usage);
|
||||||
shapes.push(ui.fonts(|f| {
|
shapes.push(ui.fonts_mut(|f| {
|
||||||
Shape::text(
|
Shape::text(
|
||||||
f,
|
f,
|
||||||
pos2(rect.left(), y),
|
pos2(rect.left(), y),
|
||||||
|
|||||||
@@ -165,14 +165,13 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
let wrap_width = 512.0;
|
let wrap_width = 512.0;
|
||||||
let font_id = egui::FontId::default();
|
let font_id = egui::FontId::default();
|
||||||
let text_color = egui::Color32::WHITE;
|
let text_color = egui::Color32::WHITE;
|
||||||
let fonts = egui::epaint::text::Fonts::new(
|
let mut fonts = egui::epaint::text::Fonts::new(
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
max_texture_side,
|
max_texture_side,
|
||||||
egui::epaint::AlphaFromCoverage::default(),
|
egui::epaint::AlphaFromCoverage::default(),
|
||||||
egui::FontDefinitions::default(),
|
egui::FontDefinitions::default(),
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
let mut locked_fonts = fonts.lock();
|
|
||||||
c.bench_function("text_layout_uncached", |b| {
|
c.bench_function("text_layout_uncached", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
use egui::epaint::text::{LayoutJob, layout};
|
use egui::epaint::text::{LayoutJob, layout};
|
||||||
@@ -183,7 +182,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
text_color,
|
text_color,
|
||||||
wrap_width,
|
wrap_width,
|
||||||
);
|
);
|
||||||
layout(&mut locked_fonts.fonts, job.into())
|
layout(&mut fonts.fonts, job.into())
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ impl crate::View for CodeEditor {
|
|||||||
language,
|
language,
|
||||||
);
|
);
|
||||||
layout_job.wrap.max_width = wrap_width;
|
layout_job.wrap.max_width = wrap_width;
|
||||||
ui.fonts(|f| f.layout_job(layout_job))
|
ui.fonts_mut(|f| f.layout_job(layout_job))
|
||||||
};
|
};
|
||||||
|
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ impl CodeExample {
|
|||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let font_id = egui::TextStyle::Monospace.resolve(ui.style());
|
let font_id = egui::TextStyle::Monospace.resolve(ui.style());
|
||||||
let indentation = 2.0 * 4.0 * ui.fonts(|f| f.glyph_width(&font_id, ' '));
|
let indentation = 2.0 * 4.0 * ui.fonts_mut(|f| f.glyph_width(&font_id, ' '));
|
||||||
ui.add_space(indentation);
|
ui.add_space(indentation);
|
||||||
|
|
||||||
egui::Grid::new("code_samples")
|
egui::Grid::new("code_samples")
|
||||||
|
|||||||
@@ -141,9 +141,8 @@ fn char_info_ui(ui: &mut egui::Ui, chr: char, glyph_info: &GlyphInfo, font_id: e
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn available_characters(ui: &egui::Ui, family: egui::FontFamily) -> BTreeMap<char, GlyphInfo> {
|
fn available_characters(ui: &egui::Ui, family: egui::FontFamily) -> BTreeMap<char, GlyphInfo> {
|
||||||
ui.fonts(|f| {
|
ui.fonts_mut(|f| {
|
||||||
f.lock()
|
f.fonts
|
||||||
.fonts
|
|
||||||
.font(&egui::FontId::new(10.0, family)) // size is arbitrary for getting the characters
|
.font(&egui::FontId::new(10.0, family)) // size is arbitrary for getting the characters
|
||||||
.characters()
|
.characters()
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ fn label_ui(ui: &mut egui::Ui) {
|
|||||||
|
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
// Trick so we don't have to add spaces in the text below:
|
// Trick so we don't have to add spaces in the text below:
|
||||||
let width = ui.fonts(|f|f.glyph_width(&TextStyle::Body.resolve(ui.style()), ' '));
|
let width = ui.fonts_mut(|f|f.glyph_width(&TextStyle::Body.resolve(ui.style()), ' '));
|
||||||
ui.spacing_mut().item_spacing.x = width;
|
ui.spacing_mut().item_spacing.x = width;
|
||||||
|
|
||||||
ui.label(RichText::new("Text can have").color(Color32::from_rgb(110, 255, 110)));
|
ui.label(RichText::new("Text can have").color(Color32::from_rgb(110, 255, 110)));
|
||||||
@@ -792,7 +792,7 @@ impl TextRotation {
|
|||||||
|
|
||||||
let start_pos = self.size / 2.0;
|
let start_pos = self.size / 2.0;
|
||||||
|
|
||||||
let s = ui.ctx().fonts(|f| {
|
let s = ui.ctx().fonts_mut(|f| {
|
||||||
let mut t = egui::Shape::text(
|
let mut t = egui::Shape::text(
|
||||||
f,
|
f,
|
||||||
rect.min + start_pos,
|
rect.min + start_pos,
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ fn huge_content_painter(ui: &mut egui::Ui) {
|
|||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
let font_id = TextStyle::Body.resolve(ui.style());
|
let font_id = TextStyle::Body.resolve(ui.style());
|
||||||
let row_height = ui.fonts(|f| f.row_height(&font_id)) + ui.spacing().item_spacing.y;
|
let row_height = ui.fonts_mut(|f| f.row_height(&font_id)) + ui.spacing().item_spacing.y;
|
||||||
let num_rows = 10_000;
|
let num_rows = 10_000;
|
||||||
|
|
||||||
ScrollArea::vertical()
|
ScrollArea::vertical()
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ impl EasyMarkEditor {
|
|||||||
let mut layouter = |ui: &egui::Ui, easymark: &dyn TextBuffer, wrap_width: f32| {
|
let mut layouter = |ui: &egui::Ui, easymark: &dyn TextBuffer, wrap_width: f32| {
|
||||||
let mut layout_job = highlighter.highlight(ui.style(), easymark.as_str());
|
let mut layout_job = highlighter.highlight(ui.style(), easymark.as_str());
|
||||||
layout_job.wrap.max_width = wrap_width;
|
layout_job.wrap.max_width = wrap_width;
|
||||||
ui.fonts(|f| f.layout_job(layout_job))
|
ui.fonts_mut(|f| f.layout_job(layout_job))
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.add(
|
ui.add(
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ fn bullet_point(ui: &mut Ui, width: f32) -> Response {
|
|||||||
|
|
||||||
fn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response {
|
fn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response {
|
||||||
let font_id = TextStyle::Body.resolve(ui.style());
|
let font_id = TextStyle::Body.resolve(ui.style());
|
||||||
let row_height = ui.fonts(|f| f.row_height(&font_id));
|
let row_height = ui.fonts_mut(|f| f.row_height(&font_id));
|
||||||
let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
|
let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
|
||||||
let text = format!("{number}.");
|
let text = format!("{number}.");
|
||||||
let text_color = ui.visuals().strong_text_color();
|
let text_color = ui.visuals().strong_text_color();
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ impl Shape {
|
|||||||
|
|
||||||
#[expect(clippy::needless_pass_by_value)]
|
#[expect(clippy::needless_pass_by_value)]
|
||||||
pub fn text(
|
pub fn text(
|
||||||
fonts: &Fonts,
|
fonts: &mut Fonts,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
anchor: Align2,
|
anchor: Align2,
|
||||||
text: impl ToString,
|
text: impl ToString,
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_bounding_box_under_rotation() {
|
fn text_bounding_box_under_rotation() {
|
||||||
let fonts = Fonts::new(
|
let mut fonts = Fonts::new(
|
||||||
1.0,
|
1.0,
|
||||||
1024,
|
1024,
|
||||||
AlphaFromCoverage::default(),
|
AlphaFromCoverage::default(),
|
||||||
@@ -190,7 +190,7 @@ mod tests {
|
|||||||
let font = FontId::monospace(12.0);
|
let font = FontId::monospace(12.0);
|
||||||
|
|
||||||
let mut t = crate::Shape::text(
|
let mut t = crate::Shape::text(
|
||||||
&fonts,
|
&mut fonts,
|
||||||
Pos2::ZERO,
|
Pos2::ZERO,
|
||||||
emath::Align2::CENTER_CENTER,
|
emath::Align2::CENTER_CENTER,
|
||||||
"testing123",
|
"testing123",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::{collections::BTreeMap, sync::Arc};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AlphaFromCoverage, TextureAtlas,
|
AlphaFromCoverage, TextureAtlas,
|
||||||
mutex::{Mutex, MutexGuard},
|
mutex::Mutex,
|
||||||
text::{
|
text::{
|
||||||
Galley, LayoutJob, LayoutSection,
|
Galley, LayoutJob, LayoutSection,
|
||||||
font::{Font, FontImpl},
|
font::{Font, FontImpl},
|
||||||
@@ -418,8 +418,10 @@ impl FontDefinitions {
|
|||||||
/// If you are using `egui`, use `egui::Context::set_fonts` and `egui::Context::fonts`.
|
/// If you are using `egui`, use `egui::Context::set_fonts` and `egui::Context::fonts`.
|
||||||
///
|
///
|
||||||
/// You need to call [`Self::begin_pass`] and [`Self::font_image_delta`] once every frame.
|
/// You need to call [`Self::begin_pass`] and [`Self::font_image_delta`] once every frame.
|
||||||
#[derive(Clone)]
|
pub struct Fonts {
|
||||||
pub struct Fonts(Arc<Mutex<FontsAndCache>>);
|
pub fonts: FontsImpl,
|
||||||
|
galley_cache: GalleyCache,
|
||||||
|
}
|
||||||
|
|
||||||
impl Fonts {
|
impl Fonts {
|
||||||
/// Create a new [`Fonts`] for text layout.
|
/// Create a new [`Fonts`] for text layout.
|
||||||
@@ -433,7 +435,7 @@ impl Fonts {
|
|||||||
text_alpha_from_coverage: AlphaFromCoverage,
|
text_alpha_from_coverage: AlphaFromCoverage,
|
||||||
definitions: FontDefinitions,
|
definitions: FontDefinitions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let fonts_and_cache = FontsAndCache {
|
Self {
|
||||||
fonts: FontsImpl::new(
|
fonts: FontsImpl::new(
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
max_texture_side,
|
max_texture_side,
|
||||||
@@ -441,8 +443,7 @@ impl Fonts {
|
|||||||
definitions,
|
definitions,
|
||||||
),
|
),
|
||||||
galley_cache: Default::default(),
|
galley_cache: Default::default(),
|
||||||
};
|
}
|
||||||
Self(Arc::new(Mutex::new(fonts_and_cache)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call at the start of each frame with the latest known
|
/// Call at the start of each frame with the latest known
|
||||||
@@ -453,27 +454,25 @@ impl Fonts {
|
|||||||
/// This function will react to changes in `pixels_per_point`, `max_texture_side`, and `text_alpha_from_coverage`,
|
/// This function will react to changes in `pixels_per_point`, `max_texture_side`, and `text_alpha_from_coverage`,
|
||||||
/// as well as notice when the font atlas is getting full, and handle that.
|
/// as well as notice when the font atlas is getting full, and handle that.
|
||||||
pub fn begin_pass(
|
pub fn begin_pass(
|
||||||
&self,
|
&mut self,
|
||||||
pixels_per_point: f32,
|
pixels_per_point: f32,
|
||||||
max_texture_side: usize,
|
max_texture_side: usize,
|
||||||
text_alpha_from_coverage: AlphaFromCoverage,
|
text_alpha_from_coverage: AlphaFromCoverage,
|
||||||
) {
|
) {
|
||||||
let mut fonts_and_cache = self.0.lock();
|
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 pixels_per_point_changed = fonts_and_cache.fonts.pixels_per_point != pixels_per_point;
|
|
||||||
let max_texture_side_changed = fonts_and_cache.fonts.max_texture_side != max_texture_side;
|
|
||||||
let text_alpha_from_coverage_changed =
|
let text_alpha_from_coverage_changed =
|
||||||
fonts_and_cache.fonts.atlas.lock().text_alpha_from_coverage != text_alpha_from_coverage;
|
self.fonts.atlas.lock().text_alpha_from_coverage != text_alpha_from_coverage;
|
||||||
let font_atlas_almost_full = fonts_and_cache.fonts.atlas.lock().fill_ratio() > 0.8;
|
let font_atlas_almost_full = self.fonts.atlas.lock().fill_ratio() > 0.8;
|
||||||
let needs_recreate = pixels_per_point_changed
|
let needs_recreate = pixels_per_point_changed
|
||||||
|| max_texture_side_changed
|
|| max_texture_side_changed
|
||||||
|| text_alpha_from_coverage_changed
|
|| text_alpha_from_coverage_changed
|
||||||
|| font_atlas_almost_full;
|
|| font_atlas_almost_full;
|
||||||
|
|
||||||
if needs_recreate {
|
if needs_recreate {
|
||||||
let definitions = fonts_and_cache.fonts.definitions.clone();
|
let definitions = self.fonts.definitions.clone();
|
||||||
|
|
||||||
*fonts_and_cache = FontsAndCache {
|
*self = Self {
|
||||||
fonts: FontsImpl::new(
|
fonts: FontsImpl::new(
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
max_texture_side,
|
max_texture_side,
|
||||||
@@ -484,83 +483,70 @@ impl Fonts {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fonts_and_cache.galley_cache.flush_cache();
|
self.galley_cache.flush_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call at the end of each frame (before painting) to get the change to the font texture since last call.
|
/// Call at the end of each frame (before painting) to get the change to the font texture since last call.
|
||||||
pub fn font_image_delta(&self) -> Option<crate::ImageDelta> {
|
pub fn font_image_delta(&self) -> Option<crate::ImageDelta> {
|
||||||
self.lock().fonts.atlas.lock().take_delta()
|
self.fonts.atlas.lock().take_delta()
|
||||||
}
|
|
||||||
|
|
||||||
/// Access the underlying [`FontsAndCache`].
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[inline]
|
|
||||||
pub fn lock(&self) -> MutexGuard<'_, FontsAndCache> {
|
|
||||||
self.0.lock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixels_per_point(&self) -> f32 {
|
pub fn pixels_per_point(&self) -> f32 {
|
||||||
self.lock().fonts.pixels_per_point
|
self.fonts.pixels_per_point
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn max_texture_side(&self) -> usize {
|
pub fn max_texture_side(&self) -> usize {
|
||||||
self.lock().fonts.max_texture_side
|
self.fonts.max_texture_side
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The font atlas.
|
/// The font atlas.
|
||||||
/// Pass this to [`crate::Tessellator`].
|
/// Pass this to [`crate::Tessellator`].
|
||||||
pub fn texture_atlas(&self) -> Arc<Mutex<TextureAtlas>> {
|
pub fn texture_atlas(&self) -> Arc<Mutex<TextureAtlas>> {
|
||||||
self.lock().fonts.atlas.clone()
|
self.fonts.atlas.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The full font atlas image.
|
/// The full font atlas image.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn image(&self) -> crate::ColorImage {
|
pub fn image(&self) -> crate::ColorImage {
|
||||||
self.lock().fonts.atlas.lock().image().clone()
|
self.fonts.atlas.lock().image().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Current size of the font image.
|
/// Current size of the font image.
|
||||||
/// Pass this to [`crate::Tessellator`].
|
/// Pass this to [`crate::Tessellator`].
|
||||||
pub fn font_image_size(&self) -> [usize; 2] {
|
pub fn font_image_size(&self) -> [usize; 2] {
|
||||||
self.lock().fonts.atlas.lock().size()
|
self.fonts.atlas.lock().size()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Width of this character in points.
|
/// Width of this character in points.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn glyph_width(&self, font_id: &FontId, c: char) -> f32 {
|
pub fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
|
||||||
self.lock().fonts.glyph_width(font_id, c)
|
self.fonts.glyph_width(font_id, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can we display this glyph?
|
/// Can we display this glyph?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn has_glyph(&self, font_id: &FontId, c: char) -> bool {
|
pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
|
||||||
self.lock().fonts.has_glyph(font_id, c)
|
self.fonts.has_glyph(font_id, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can we display all the glyphs in this text?
|
/// Can we display all the glyphs in this text?
|
||||||
pub fn has_glyphs(&self, font_id: &FontId, s: &str) -> bool {
|
pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
|
||||||
self.lock().fonts.has_glyphs(font_id, s)
|
self.fonts.has_glyphs(font_id, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Height of one row of text in points.
|
/// Height of one row of text in points.
|
||||||
///
|
///
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn row_height(&self, font_id: &FontId) -> f32 {
|
pub fn row_height(&mut self, font_id: &FontId) -> f32 {
|
||||||
self.lock().fonts.row_height(font_id)
|
self.fonts.row_height(font_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List of all known font families.
|
/// List of all known font families.
|
||||||
pub fn families(&self) -> Vec<FontFamily> {
|
pub fn families(&self) -> Vec<FontFamily> {
|
||||||
self.lock()
|
self.fonts.definitions.families.keys().cloned().collect()
|
||||||
.fonts
|
|
||||||
.definitions
|
|
||||||
.families
|
|
||||||
.keys()
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout some text.
|
/// Layout some text.
|
||||||
@@ -571,12 +557,14 @@ impl Fonts {
|
|||||||
///
|
///
|
||||||
/// The implementation uses memoization so repeated calls are cheap.
|
/// The implementation uses memoization so repeated calls are cheap.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn layout_job(&self, job: LayoutJob) -> Arc<Galley> {
|
pub fn layout_job(&mut self, job: LayoutJob) -> Arc<Galley> {
|
||||||
self.lock().layout_job(job)
|
let allow_split_paragraphs = true; // Optimization for editing text with many paragraphs.
|
||||||
|
self.galley_cache
|
||||||
|
.layout(&mut self.fonts, job, allow_split_paragraphs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_galleys_in_cache(&self) -> usize {
|
pub fn num_galleys_in_cache(&self) -> usize {
|
||||||
self.lock().galley_cache.num_galleys_in_cache()
|
self.galley_cache.num_galleys_in_cache()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How full is the font atlas?
|
/// How full is the font atlas?
|
||||||
@@ -584,14 +572,14 @@ impl Fonts {
|
|||||||
/// This increases as new fonts and/or glyphs are used,
|
/// This increases as new fonts and/or glyphs are used,
|
||||||
/// but can also decrease in a call to [`Self::begin_pass`].
|
/// but can also decrease in a call to [`Self::begin_pass`].
|
||||||
pub fn font_atlas_fill_ratio(&self) -> f32 {
|
pub fn font_atlas_fill_ratio(&self) -> f32 {
|
||||||
self.lock().fonts.atlas.lock().fill_ratio()
|
self.fonts.atlas.lock().fill_ratio()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Will wrap text at the given width and line break at `\n`.
|
/// Will wrap text at the given width and line break at `\n`.
|
||||||
///
|
///
|
||||||
/// The implementation uses memoization so repeated calls are cheap.
|
/// The implementation uses memoization so repeated calls are cheap.
|
||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&mut self,
|
||||||
text: String,
|
text: String,
|
||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
color: crate::Color32,
|
color: crate::Color32,
|
||||||
@@ -605,7 +593,7 @@ impl Fonts {
|
|||||||
///
|
///
|
||||||
/// The implementation uses memoization so repeated calls are cheap.
|
/// The implementation uses memoization so repeated calls are cheap.
|
||||||
pub fn layout_no_wrap(
|
pub fn layout_no_wrap(
|
||||||
&self,
|
&mut self,
|
||||||
text: String,
|
text: String,
|
||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
color: crate::Color32,
|
color: crate::Color32,
|
||||||
@@ -618,7 +606,7 @@ impl Fonts {
|
|||||||
///
|
///
|
||||||
/// The implementation uses memoization so repeated calls are cheap.
|
/// The implementation uses memoization so repeated calls are cheap.
|
||||||
pub fn layout_delayed_color(
|
pub fn layout_delayed_color(
|
||||||
&self,
|
&mut self,
|
||||||
text: String,
|
text: String,
|
||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
wrap_width: f32,
|
wrap_width: f32,
|
||||||
@@ -629,19 +617,6 @@ impl Fonts {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
pub struct FontsAndCache {
|
|
||||||
pub fonts: FontsImpl,
|
|
||||||
galley_cache: GalleyCache,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FontsAndCache {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// The collection of fonts used by `epaint`.
|
/// The collection of fonts used by `epaint`.
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ impl LayoutJob {
|
|||||||
/// The height of the tallest font used in the job.
|
/// The height of the tallest font used in the job.
|
||||||
///
|
///
|
||||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||||
pub fn font_height(&self, fonts: &crate::Fonts) -> f32 {
|
pub fn font_height(&self, fonts: &mut crate::Fonts) -> f32 {
|
||||||
let mut max_height = 0.0_f32;
|
let mut max_height = 0.0_f32;
|
||||||
for section in &self.sections {
|
for section in &self.sections {
|
||||||
max_height = max_height.max(fonts.row_height(§ion.format.font_id));
|
max_height = max_height.max(fonts.row_height(§ion.format.font_id));
|
||||||
|
|||||||
Reference in New Issue
Block a user