diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index e8b0803b958..7e7eff2eb74 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -9,7 +9,7 @@ use epaint::{ pos2, stats::PaintStats, tessellator, - text::Fonts, + text::{FontInsert, FontPriority, Fonts}, util::OrderedFloat, vec2, ClippedPrimitive, ClippedShape, Color32, ImageData, ImageDelta, Pos2, Rect, TessellationOptions, TextureAtlas, TextureId, Vec2, @@ -582,6 +582,28 @@ impl ContextImpl { log::trace!("Loading new font definitions"); } + 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 + for family in font.families { + let fam = self + .font_definitions + .families + .entry(family.family) + .or_default(); + match family.priority { + FontPriority::Highest => fam.insert(0, font.name.clone()), + FontPriority::Lowest => fam.push(font.name.clone()), + } + } + self.font_definitions.font_data.insert(font.name, font.data); + } + + #[cfg(feature = "log")] + log::trace!("Adding new fonts"); + } + let mut is_new = false; let fonts = self @@ -1727,6 +1749,7 @@ impl Context { /// but you can call this to install additional fonts that support e.g. korean characters. /// /// The new fonts will become active at the start of the next pass. + /// This will overwrite the existing fonts. pub fn set_fonts(&self, font_definitions: FontDefinitions) { crate::profile_function!(); @@ -1748,6 +1771,39 @@ impl Context { } } + /// Tell `egui` which fonts to use. + /// + /// The default `egui` fonts only support latin and cyrillic alphabets, + /// but you can call this to install additional fonts that support e.g. korean characters. + /// + /// The new font will become active at the start of the next pass. + /// This will keep the existing fonts. + pub fn add_font(&self, new_font: FontInsert) { + crate::profile_function!(); + + 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 current_fonts + .lock() + .fonts + .definitions() + .font_data + .contains_key(&new_font.name) + { + update_fonts = false; // no need to update + } + } + }); + + if update_fonts { + self.memory_mut(|mem| mem.add_fonts.push(new_font)); + } + } + /// Does the OS use dark or light mode? /// This is used when the theme preference is set to [`crate::ThemePreference::System`]. pub fn system_theme(&self) -> Option { diff --git a/crates/egui/src/memory/mod.rs b/crates/egui/src/memory/mod.rs index 45b10dc8eba..75183bdd15b 100644 --- a/crates/egui/src/memory/mod.rs +++ b/crates/egui/src/memory/mod.rs @@ -79,6 +79,10 @@ pub struct Memory { #[cfg_attr(feature = "persistence", serde(skip))] pub(crate) new_font_definitions: Option, + /// add new font that will be applied at the start of the next frame + #[cfg_attr(feature = "persistence", serde(skip))] + pub(crate) add_fonts: Vec, + // Current active viewport #[cfg_attr(feature = "persistence", serde(skip))] pub(crate) viewport_id: ViewportId, @@ -119,6 +123,7 @@ impl Default for Memory { layer_transforms: Default::default(), popup: Default::default(), everything_is_visible: Default::default(), + add_fonts: Default::default(), }; slf.interactions.entry(slf.viewport_id).or_default(); slf.areas.entry(slf.viewport_id).or_default(); diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 42398e5f4db..685bdf9b8a8 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -254,6 +254,50 @@ pub struct FontDefinitions { pub families: BTreeMap>, } +#[derive(Debug, Clone)] +pub struct FontInsert { + /// Font name + pub name: String, + + /// A `.ttf` or `.otf` file and a font face index. + pub data: FontData, + + /// Sets the font family and priority + pub families: Vec, +} + +#[derive(Debug, Clone)] +pub struct InsertFontFamily { + /// Font family + pub family: FontFamily, + + /// Fallback or Primary font + pub priority: FontPriority, +} + +#[derive(Debug, Clone)] +pub enum FontPriority { + /// Prefer this font before all existing ones. + /// + /// If a desired glyph exists in this font, it will be used. + Highest, + + /// Use this font as a fallback, after all existing ones. + /// + /// This font will only be used if the glyph is not found in any of the previously installed fonts. + Lowest, +} + +impl FontInsert { + pub fn new(name: &str, data: FontData, families: Vec) -> Self { + Self { + name: name.to_owned(), + data, + families, + } + } +} + impl Default for FontDefinitions { /// Specifies the default fonts if the feature `default_fonts` is enabled, /// otherwise this is the same as [`Self::empty`]. diff --git a/crates/epaint/src/text/mod.rs b/crates/epaint/src/text/mod.rs index ca1bc314c22..3cb0e98cbc5 100644 --- a/crates/epaint/src/text/mod.rs +++ b/crates/epaint/src/text/mod.rs @@ -10,7 +10,10 @@ mod text_layout_types; pub const TAB_SIZE: usize = 4; pub use { - fonts::{FontData, FontDefinitions, FontFamily, FontId, FontTweak, Fonts, FontsImpl}, + fonts::{ + FontData, FontDefinitions, FontFamily, FontId, FontInsert, FontPriority, FontTweak, Fonts, + FontsImpl, InsertFontFamily, + }, text_layout::layout, text_layout_types::*, }; diff --git a/examples/custom_font/src/main.rs b/examples/custom_font/src/main.rs index 5afa72905af..99c98a6e158 100644 --- a/examples/custom_font/src/main.rs +++ b/examples/custom_font/src/main.rs @@ -1,7 +1,10 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![allow(rustdoc::missing_crate_level_docs)] // it's an example -use eframe::egui; +use eframe::{ + egui, + epaint::text::{FontInsert, InsertFontFamily}, +}; fn main() -> eframe::Result { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). @@ -16,7 +19,28 @@ fn main() -> eframe::Result { ) } -fn setup_custom_fonts(ctx: &egui::Context) { +// Demonstrates how to add a font to the existing ones +fn add_font(ctx: &egui::Context) { + ctx.add_font(FontInsert::new( + "my_font", + egui::FontData::from_static(include_bytes!( + "../../../crates/epaint_default_fonts/fonts/Hack-Regular.ttf" + )), + vec![ + InsertFontFamily { + family: egui::FontFamily::Proportional, + priority: egui::epaint::text::FontPriority::Highest, + }, + InsertFontFamily { + family: egui::FontFamily::Monospace, + priority: egui::epaint::text::FontPriority::Lowest, + }, + ], + )); +} + +// Demonstrates how to replace all fonts. +fn replace_fonts(ctx: &egui::Context) { // Start with the default fonts (we will be adding to them rather than replacing them). let mut fonts = egui::FontDefinitions::default(); @@ -53,7 +77,8 @@ struct MyApp { impl MyApp { fn new(cc: &eframe::CreationContext<'_>) -> Self { - setup_custom_fonts(&cc.egui_ctx); + replace_fonts(&cc.egui_ctx); + add_font(&cc.egui_ctx); Self { text: "Edit this text field if you want".to_owned(), }