Skip to content

Commit a461fed

Browse files
committed
First part of font loading/fallback support
Also supports a lot more text styling but still not all of it.
1 parent 7db72ac commit a461fed

34 files changed

+1976
-1236
lines changed

Cargo.lock

+45-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is automatically @generated by Cargo.
22
# It is not intended for manual editing.
3-
version = 3
3+
version = 4
44

55
[[package]]
66
name = "ab_glyph"
@@ -1517,7 +1517,6 @@ dependencies = [
15171517
name = "epaint"
15181518
version = "0.31.1"
15191519
dependencies = [
1520-
"ab_glyph",
15211520
"accesskit",
15221521
"ahash",
15231522
"backtrace",
@@ -1527,14 +1526,15 @@ dependencies = [
15271526
"ecolor",
15281527
"emath",
15291528
"epaint_default_fonts",
1529+
"etagere",
15301530
"log",
15311531
"nohash-hasher",
15321532
"parking_lot",
15331533
"parley",
15341534
"profiling",
15351535
"rayon",
15361536
"serde",
1537-
"swash",
1537+
"swash 0.2.1 (git+https://github.com/valadaptive/swash?branch=tight-bounds)",
15381538
]
15391539

15401540
[[package]]
@@ -1563,6 +1563,25 @@ version = "3.3.1"
15631563
source = "registry+https://github.com/rust-lang/crates.io-index"
15641564
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
15651565

1566+
[[package]]
1567+
name = "etagere"
1568+
version = "0.2.15"
1569+
source = "registry+https://github.com/rust-lang/crates.io-index"
1570+
checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342"
1571+
dependencies = [
1572+
"euclid",
1573+
"svg_fmt",
1574+
]
1575+
1576+
[[package]]
1577+
name = "euclid"
1578+
version = "0.22.11"
1579+
source = "registry+https://github.com/rust-lang/crates.io-index"
1580+
checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48"
1581+
dependencies = [
1582+
"num-traits",
1583+
]
1584+
15661585
[[package]]
15671586
name = "event-listener"
15681587
version = "5.3.1"
@@ -3086,7 +3105,7 @@ dependencies = [
30863105
"peniko",
30873106
"serde",
30883107
"skrifa",
3089-
"swash",
3108+
"swash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
30903109
]
30913110

30923111
[[package]]
@@ -3937,6 +3956,12 @@ version = "2.6.1"
39373956
source = "registry+https://github.com/rust-lang/crates.io-index"
39383957
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
39393958

3959+
[[package]]
3960+
name = "svg_fmt"
3961+
version = "0.4.4"
3962+
source = "registry+https://github.com/rust-lang/crates.io-index"
3963+
checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa"
3964+
39403965
[[package]]
39413966
name = "svgtypes"
39423967
version = "0.13.0"
@@ -3955,7 +3980,17 @@ checksum = "13d5bbc2aa266907ed8ee977c9c9e16363cc2b001266104e13397b57f1d15f71"
39553980
dependencies = [
39563981
"skrifa",
39573982
"yazi",
3958-
"zeno",
3983+
"zeno 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
3984+
]
3985+
3986+
[[package]]
3987+
name = "swash"
3988+
version = "0.2.1"
3989+
source = "git+https://github.com/valadaptive/swash?branch=tight-bounds#46eafa8b69e07b31da163556cb85989a9f99ba42"
3990+
dependencies = [
3991+
"skrifa",
3992+
"yazi",
3993+
"zeno 0.3.2 (git+https://github.com/valadaptive/zeno?branch=tight-bounds)",
39593994
]
39603995

39613996
[[package]]
@@ -5412,6 +5447,11 @@ version = "0.3.2"
54125447
source = "registry+https://github.com/rust-lang/crates.io-index"
54135448
checksum = "cc0de2315dc13d00e5df3cd6b8d2124a6eaec6a2d4b6a1c5f37b7efad17fcc17"
54145449

5450+
[[package]]
5451+
name = "zeno"
5452+
version = "0.3.2"
5453+
source = "git+https://github.com/valadaptive/zeno?branch=tight-bounds#1c5e5eb226e05e21a8efa1925df36a58d6bbbd72"
5454+
54155455
[[package]]
54165456
name = "zerocopy"
54175457
version = "0.7.35"

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@ Light Theme:
145145
## Dependencies
146146
`egui` has a minimal set of default dependencies:
147147

148-
* [`ab_glyph`](https://crates.io/crates/ab_glyph)
149148
* [`ahash`](https://crates.io/crates/ahash)
150149
* [`bitflags`](https://crates.io/crates/bitflags)
151150
* [`nohash-hasher`](https://crates.io/crates/nohash-hasher)
152151
* [`parking_lot`](https://crates.io/crates/parking_lot)
152+
* [`parley`](https://crates.io/crates/parley)
153153

154154
Heavier dependencies are kept out of `egui`, even as opt-in.
155155
All code in `egui` is Wasm-friendly (even outside a browser).

crates/egui/src/context.rs

+42-33
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,6 @@ impl ViewportRepaintInfo {
394394
#[derive(Default)]
395395
struct ContextImpl {
396396
fonts: Option<FontStore>,
397-
font_definitions: FontDefinitions,
398397

399398
memory: Memory,
400399
animation_manager: AnimationManager,
@@ -552,33 +551,29 @@ impl ContextImpl {
552551
fn update_fonts_mut(&mut self) {
553552
profiling::function_scope!();
554553
let input = &self.viewport().input;
555-
let pixels_per_point = input.pixels_per_point();
556554
let max_texture_side = input.max_texture_side;
557555

558-
if let Some(font_definitions) = self.memory.new_font_definitions.take() {
559-
// New font definition loaded, so we need to reload all fonts.
560-
self.fonts = None;
561-
self.font_definitions = font_definitions;
562-
#[cfg(feature = "log")]
563-
log::trace!("Loading new font definitions");
564-
}
556+
let mut new_font_definitions =
557+
if let Some(font_definitions) = self.memory.new_font_definitions.take() {
558+
Cow::Owned(font_definitions)
559+
} else if let Some(fonts) = &self.fonts {
560+
Cow::Borrowed(fonts.definitions())
561+
} else {
562+
Cow::Owned(FontDefinitions::default())
563+
};
565564

566565
if !self.memory.add_fonts.is_empty() {
567566
let fonts = self.memory.add_fonts.drain(..);
567+
let font_definitions = new_font_definitions.to_mut();
568568
for font in fonts {
569-
self.fonts = None; // recreate all the fonts
570569
for family in font.families {
571-
let fam = self
572-
.font_definitions
573-
.families
574-
.entry(family.family)
575-
.or_default();
570+
let fam = font_definitions.families.entry(family.family).or_default();
576571
match family.priority {
577572
FontPriority::Highest => fam.insert(0, font.name.clone()),
578573
FontPriority::Lowest => fam.push(font.name.clone()),
579574
}
580575
}
581-
self.font_definitions
576+
font_definitions
582577
.font_data
583578
.insert(font.name, Arc::new(font.data));
584579
}
@@ -587,28 +582,43 @@ impl ContextImpl {
587582
log::trace!("Adding new fonts");
588583
}
589584

590-
let mut is_new = false;
585+
let mut did_change_fonts = false;
591586

592-
let fonts = self.fonts.get_or_insert_with(|| {
593-
#[cfg(feature = "log")]
594-
log::trace!("Creating new Fonts for pixels_per_point={pixels_per_point}");
587+
// If we changed the font definitions this frame, or self.fonts previously did not exist, new_font_definitions
588+
// must be Cow::Owned. Set the font definitions.
589+
let fonts = if let Cow::Owned(new_font_definitions) = new_font_definitions {
590+
did_change_fonts = true;
591+
if let Some(fonts) = self.fonts.as_mut() {
592+
fonts.set_definitions(new_font_definitions);
593+
did_change_fonts = true;
594+
fonts
595+
} else {
596+
self.fonts.get_or_insert_with(|| {
597+
#[cfg(feature = "log")]
598+
log::trace!("Creating new FontStore");
595599

596-
is_new = true;
597-
profiling::scope!("FontStore::new");
598-
FontStore::new(max_texture_side, self.font_definitions.clone())
599-
});
600+
did_change_fonts = true;
601+
profiling::scope!("FontStore::new");
602+
FontStore::new(max_texture_side, new_font_definitions)
603+
})
604+
}
605+
} else {
606+
self.fonts
607+
.as_mut()
608+
.expect("new_font_definitions is borrowed, but we had nowhere to borrow it from")
609+
};
600610

601611
{
602612
profiling::scope!("FontStore::begin_pass");
603613
fonts.begin_pass(max_texture_side);
604614
}
605615

606-
if is_new && self.memory.options.preload_font_glyphs {
616+
if did_change_fonts && self.memory.options.preload_font_glyphs {
607617
profiling::scope!("preload_font_glyphs");
608618
// Preload the most common characters for the most common fonts.
609619
// This is not very important to do, but may save a few GPU operations.
610-
for font_id in self.memory.options.style().text_styles.values() {
611-
fonts.font(font_id).preload_common_characters();
620+
for font_style in self.memory.options.style().text_styles.values() {
621+
fonts.preload_common_characters(font_style);
612622
}
613623
}
614624
}
@@ -1495,11 +1505,10 @@ impl Context {
14951505

14961506
let font_id = TextStyle::Body.resolve(&self.style());
14971507
self.fonts(|f| {
1498-
let font = f.font(&font_id);
1499-
font.has_glyphs(alt)
1500-
&& font.has_glyphs(ctrl)
1501-
&& font.has_glyphs(shift)
1502-
&& font.has_glyphs(mac_cmd)
1508+
f.has_glyphs_for(&font_id, alt)
1509+
&& f.has_glyphs_for(&font_id, ctrl)
1510+
&& f.has_glyphs_for(&font_id, shift)
1511+
&& f.has_glyphs_for(&font_id, mac_cmd)
15031512
})
15041513
};
15051514

@@ -2909,7 +2918,7 @@ impl Context {
29092918
}
29102919

29112920
fn fonts_tweak_ui(&self, ui: &mut Ui) {
2912-
let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
2921+
let mut font_definitions = self.fonts(|fonts| fonts.definitions().clone());
29132922
let mut changed = false;
29142923

29152924
for (name, data) in &mut font_definitions.font_data {

crates/egui/src/debug_text.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
//! to get callbacks on certain events ([`Context::on_begin_pass`], [`Context::on_end_pass`]).
77
88
use crate::{
9-
text, Align, Align2, Color32, Context, FontFamily, FontId, Id, Rect, Shape, Vec2, WidgetText,
9+
text::{self, style::FontId},
10+
Align, Align2, Color32, Context, Id, Rect, Shape, Vec2, WidgetText,
1011
};
1112

1213
/// Register this plugin on the given egui context,
@@ -92,7 +93,7 @@ impl State {
9293
let mut bounding_rect = Rect::from_points(&[pos]);
9394

9495
let color = Color32::GRAY;
95-
let font_id = FontId::new(10.0, FontFamily::Proportional);
96+
let font_id = FontId::system_ui(10.0);
9697

9798
for Entry { location, text } in entries {
9899
{

crates/egui/src/input_state/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,7 @@ impl InputState {
13861386
.text_styles
13871387
.get_mut(&crate::TextStyle::Body)
13881388
.unwrap()
1389-
.family = crate::FontFamily::Monospace;
1389+
.family = crate::text::style::GenericFamily::Monospace.into();
13901390

13911391
ui.collapsing("Raw Input", |ui| raw.ui(ui));
13921392

crates/egui/src/introspection.rs

+22-18
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
//! Showing UI:s for egui/epaint types.
2+
3+
use epaint::text::style::GenericFamily;
4+
25
use crate::{
3-
epaint, memory, pos2, remap_clamp, vec2, Color32, CursorIcon, FontFamily, FontId, Label, Mesh,
4-
NumExt, Rect, Response, Sense, Shape, Slider, TextStyle, TextWrapMode, Ui, Widget,
6+
epaint, memory, pos2, remap_clamp, text::style::FontId, vec2, Color32, ComboBox, CursorIcon,
7+
Label, Mesh, NumExt, Rect, Response, Sense, Shape, Slider, TextStyle, TextWrapMode, Ui, Widget,
58
};
69

7-
pub fn font_family_ui(ui: &mut Ui, font_family: &mut FontFamily) {
8-
let families = ui.fonts(|f| f.families());
9-
ui.horizontal(|ui| {
10-
for alternative in families {
11-
let text = alternative.to_string();
12-
ui.radio_value(font_family, alternative, text);
13-
}
14-
});
15-
}
16-
1710
pub fn font_id_ui(ui: &mut Ui, font_id: &mut FontId) {
18-
let families = ui.fonts(|f| f.families());
19-
ui.horizontal(|ui| {
11+
let families = ui.fonts(|f| f.families().to_owned());
12+
ui.horizontal_wrapped(|ui| {
2013
ui.add(Slider::new(&mut font_id.size, 4.0..=40.0).max_decimals(1));
21-
for alternative in families {
22-
let text = alternative.to_string();
23-
ui.radio_value(&mut font_id.family, alternative, text);
24-
}
14+
ComboBox::from_id_salt(ui.next_auto_id())
15+
.selected_text(font_id.family.first_family().to_string())
16+
.show_ui(ui, |ui| {
17+
for generic in GenericFamily::ALL {
18+
let text = generic.to_string();
19+
ui.selectable_value(&mut font_id.family, generic.into(), text);
20+
}
21+
22+
ui.separator();
23+
24+
for alternative in families {
25+
let text = alternative.to_string();
26+
ui.selectable_value(&mut font_id.family, alternative.into(), text);
27+
}
28+
});
2529
});
2630
}
2731

crates/egui/src/lib.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ pub use emath::{
464464
};
465465
pub use epaint::{
466466
mutex,
467-
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
467+
text::{FontData, FontDefinitions, FontTweak},
468468
textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta},
469469
ClippedPrimitive, ColorImage, CornerRadius, ImageData, Margin, Mesh, PaintCallback,
470470
PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId,
@@ -473,8 +473,9 @@ pub use epaint::{
473473
pub mod text {
474474
pub use epaint::text::{
475475
cursor::{ByteCursor, Selection},
476-
FontData, FontDefinitions, FontFamily, FontStore, Galley, LayoutJob, LayoutSection,
477-
TextFormat, TextWrapping, TAB_SIZE,
476+
style::{self, TextFormat},
477+
FontData, FontDefinitions, FontStore, Galley, LayoutJob, LayoutSection, TextWrapping,
478+
TAB_SIZE,
478479
};
479480
}
480481

crates/egui/src/memory/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,10 @@ pub struct Options {
277277
///
278278
/// This can lead to fewer texture operations, but may use up the texture atlas quicker
279279
/// if you are changing [`Style::text_styles`], or have a lot of text styles.
280+
///
281+
/// TODO(valadaptive): preload_font_glyphs used to do something, but the new text layout code rasterizes at subpixel
282+
/// offsets, and I don't feel like rasterizing all 4 offsets for every glyph ahead of time. Is preload_font_glyphs
283+
/// actually useful or just a placebo?
280284
pub preload_font_glyphs: bool,
281285

282286
/// Check reusing of [`Id`]s, and show a visual warning on screen when one is found.

crates/egui/src/painter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use std::sync::Arc;
22

33
use emath::GuiRounding as _;
44
use epaint::{
5-
text::{Fonts, Galley, LayoutJob},
5+
text::{style::FontId, Fonts, Galley, LayoutJob},
66
CircleShape, ClippedShape, CornerRadius, PathStroke, RectShape, Shape, Stroke, StrokeKind,
77
};
88

99
use crate::{
1010
emath::{Align2, Pos2, Rangef, Rect, Vec2},
1111
layers::{LayerId, PaintList, ShapeIdx},
12-
Color32, Context, FontId,
12+
Color32, Context,
1313
};
1414

1515
/// Helper to paint shapes and text to a specific region on a specific layer.

crates/egui/src/pass_state.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use ahash::HashMap;
22

3-
use crate::{id::IdSet, style, Align, Id, IdMap, LayerId, Rangef, Rect, Vec2, WidgetRects};
3+
use crate::{
4+
id::IdSet, style, text::style::FontId, Align, Id, IdMap, LayerId, Rangef, Rect, Vec2,
5+
WidgetRects,
6+
};
47

58
#[cfg(debug_assertions)]
6-
use crate::{pos2, Align2, Color32, FontId, NumExt, Painter};
9+
use crate::{pos2, Align2, Color32, NumExt, Painter};
710

811
/// Reset at the start of each frame.
912
#[derive(Clone, Debug, Default)]

0 commit comments

Comments
 (0)