From e1079db93a152f4dab91668e6fc4af271e745318 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 21 Nov 2023 23:56:30 -0500 Subject: [PATCH] Add font loading & configuration --- Cargo.lock | 158 ++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/app.rs | 14 +-- src/fonts/matching.rs | 146 +++++++++++++++++++++++++++++ src/fonts/mod.rs | 104 +++++++++++++++++++++ src/lib.rs | 1 + src/views/appearance.rs | 183 +++++++++++++++++++++++++++++++++++-- src/views/function_diff.rs | 3 +- 8 files changed, 592 insertions(+), 19 deletions(-) create mode 100644 src/fonts/matching.rs create mode 100644 src/fonts/mod.rs diff --git a/Cargo.lock b/Cargo.lock index d22ee12..1333354 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -707,6 +707,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "cocoa" version = "0.24.1" @@ -801,6 +810,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-cstr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" + [[package]] name = "const_format" version = "0.2.32" @@ -861,6 +876,18 @@ dependencies = [ "libc", ] +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types 0.3.2", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.11" @@ -977,6 +1004,16 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-sys" version = "0.4.1" @@ -1021,6 +1058,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "ecolor" version = "0.23.0" @@ -1374,12 +1423,49 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-ord" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "font-kit" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord 0.2.0", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -1431,6 +1517,27 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -2674,6 +2781,8 @@ dependencies = [ "exec", "filetime", "flagset", + "float-ord 0.3.2", + "font-kit", "globset", "log", "memmap2 0.9.0", @@ -2857,6 +2966,25 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0444332826c70dc47be74a7c6a5fc44e23a7905ad6858d4162b658320455ef93" +dependencies = [ + "rustc_version", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -3232,6 +3360,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.27" @@ -4768,6 +4905,15 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -4826,6 +4972,18 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +[[package]] +name = "yeslogic-fontconfig-sys" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" +dependencies = [ + "const-cstr", + "dlib", + "once_cell", + "pkg-config", +] + [[package]] name = "zbus" version = "3.14.1" diff --git a/Cargo.toml b/Cargo.toml index 7769bae..c4f7b95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,8 @@ egui = "0.23.0" egui_extras = "0.23.0" filetime = "0.2.22" flagset = "0.4.4" +float-ord = "0.3.2" +font-kit = "0.11.0" globset = { version = "0.4.13", features = ["serde1"] } log = "0.4.20" memmap2 = "0.9.0" diff --git a/src/app.rs b/src/app.rs index 3a5b6f9..6348b64 100644 --- a/src/app.rs +++ b/src/app.rs @@ -223,9 +223,6 @@ impl App { utc_offset: UtcOffset, relaunch_path: Rc>>, ) -> Self { - // This is also where you can customized the look at feel of egui using - // `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`. - // Load previous app state (if any). // Note that you must enable the `persistence` feature for this to work. let mut app = Self::default(); @@ -245,12 +242,15 @@ impl App { app.config = Arc::new(RwLock::new(config)); } } + app.appearance.init_fonts(&cc.egui_ctx); app.appearance.utc_offset = utc_offset; app.relaunch_path = relaunch_path; app } - fn pre_update(&mut self) { + fn pre_update(&mut self, ctx: &egui::Context) { + self.appearance.pre_update(ctx); + let ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state; let mut results = vec![]; @@ -306,6 +306,8 @@ impl App { } fn post_update(&mut self, ctx: &egui::Context) { + self.appearance.post_update(ctx); + let ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state; config_state.post_update(ctx, jobs, &self.config); diff_state.post_update(&self.config); @@ -400,11 +402,9 @@ impl eframe::App for App { return; } - self.pre_update(); + self.pre_update(ctx); let Self { config, appearance, view_state, .. } = self; - ctx.set_style(appearance.apply(ctx.style().as_ref())); - let ViewState { jobs, config_state, diff --git a/src/fonts/matching.rs b/src/fonts/matching.rs new file mode 100644 index 0000000..7a76afa --- /dev/null +++ b/src/fonts/matching.rs @@ -0,0 +1,146 @@ +// font-kit/src/matching.rs +// +// Copyright © 2018 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Determines the closest font matching a description per the CSS Fonts Level 3 specification. + +use float_ord::FloatOrd; +use font_kit::{ + error::SelectionError, + properties::{Properties, Stretch, Style, Weight}, +}; + +/// This follows CSS Fonts Level 3 § 5.2 [1]. +/// +/// https://drafts.csswg.org/css-fonts-3/#font-style-matching +pub fn find_best_match( + candidates: &[Properties], + query: &Properties, +) -> Result { + // Step 4. + let mut matching_set: Vec = (0..candidates.len()).collect(); + if matching_set.is_empty() { + return Err(SelectionError::NotFound); + } + + // Step 4a (`font-stretch`). + let matching_stretch = if matching_set + .iter() + .any(|&index| candidates[index].stretch == query.stretch) + { + // Exact match. + query.stretch + } else if query.stretch <= Stretch::NORMAL { + // Closest width, first checking narrower values and then wider values. + match matching_set + .iter() + .filter(|&&index| candidates[index].stretch < query.stretch) + .min_by_key(|&&index| FloatOrd(query.stretch.0 - candidates[index].stretch.0)) + { + Some(&matching_index) => candidates[matching_index].stretch, + None => { + let matching_index = *matching_set + .iter() + .min_by_key(|&&index| FloatOrd(candidates[index].stretch.0 - query.stretch.0)) + .unwrap(); + candidates[matching_index].stretch + } + } + } else { + // Closest width, first checking wider values and then narrower values. + match matching_set + .iter() + .filter(|&&index| candidates[index].stretch > query.stretch) + .min_by_key(|&&index| FloatOrd(candidates[index].stretch.0 - query.stretch.0)) + { + Some(&matching_index) => candidates[matching_index].stretch, + None => { + let matching_index = *matching_set + .iter() + .min_by_key(|&&index| FloatOrd(query.stretch.0 - candidates[index].stretch.0)) + .unwrap(); + candidates[matching_index].stretch + } + } + }; + matching_set.retain(|&index| candidates[index].stretch == matching_stretch); + + // Step 4b (`font-style`). + let style_preference = match query.style { + Style::Italic => [Style::Italic, Style::Oblique, Style::Normal], + Style::Oblique => [Style::Oblique, Style::Italic, Style::Normal], + Style::Normal => [Style::Normal, Style::Oblique, Style::Italic], + }; + let matching_style = *style_preference + .iter() + .find(|&query_style| { + matching_set.iter().any(|&index| candidates[index].style == *query_style) + }) + .unwrap(); + matching_set.retain(|&index| candidates[index].style == matching_style); + + // Step 4c (`font-weight`). + // + // The spec doesn't say what to do if the weight is between 400 and 500 exclusive, so we + // just use 450 as the cutoff. + let matching_weight = + if matching_set.iter().any(|&index| candidates[index].weight == query.weight) { + query.weight + } else if query.weight >= Weight(400.0) + && query.weight < Weight(450.0) + && matching_set.iter().any(|&index| candidates[index].weight == Weight(500.0)) + { + // Check 500 first. + Weight(500.0) + } else if query.weight >= Weight(450.0) + && query.weight <= Weight(500.0) + && matching_set.iter().any(|&index| candidates[index].weight == Weight(400.0)) + { + // Check 400 first. + Weight(400.0) + } else if query.weight <= Weight(500.0) { + // Closest weight, first checking thinner values and then fatter ones. + match matching_set + .iter() + .filter(|&&index| candidates[index].weight <= query.weight) + .min_by_key(|&&index| FloatOrd(query.weight.0 - candidates[index].weight.0)) + { + Some(&matching_index) => candidates[matching_index].weight, + None => { + let matching_index = *matching_set + .iter() + .min_by_key(|&&index| FloatOrd(candidates[index].weight.0 - query.weight.0)) + .unwrap(); + candidates[matching_index].weight + } + } + } else { + // Closest weight, first checking fatter values and then thinner ones. + match matching_set + .iter() + .filter(|&&index| candidates[index].weight >= query.weight) + .min_by_key(|&&index| FloatOrd(candidates[index].weight.0 - query.weight.0)) + { + Some(&matching_index) => candidates[matching_index].weight, + None => { + let matching_index = *matching_set + .iter() + .min_by_key(|&&index| FloatOrd(query.weight.0 - candidates[index].weight.0)) + .unwrap(); + candidates[matching_index].weight + } + } + }; + matching_set.retain(|&index| candidates[index].weight == matching_weight); + + // Step 4d concerns `font-size`, but fonts in `font-kit` are unsized, so we ignore that. + + // Return the result. + matching_set.into_iter().next().ok_or(SelectionError::NotFound) +} diff --git a/src/fonts/mod.rs b/src/fonts/mod.rs new file mode 100644 index 0000000..0bdb5c9 --- /dev/null +++ b/src/fonts/mod.rs @@ -0,0 +1,104 @@ +pub mod matching; + +use std::{borrow::Cow, fs, sync::Arc}; + +use anyhow::{Context, Result}; + +use crate::fonts::matching::find_best_match; + +pub struct LoadedFontFamily { + pub family_name: String, + pub fonts: Vec, + pub handles: Vec, + pub properties: Vec, + pub default_index: usize, +} + +pub struct LoadedFont { + pub font_name: String, + pub font_data: egui::FontData, +} + +pub fn load_font_family( + source: &font_kit::source::SystemSource, + name: &str, +) -> Option { + let family_handle = source.select_family_by_name(name).ok()?; + if family_handle.fonts().is_empty() { + log::warn!("No fonts found for family '{}'", name); + return None; + } + let handles = family_handle.fonts().to_vec(); + let mut loaded = Vec::with_capacity(handles.len()); + for handle in handles.iter() { + match font_kit::loaders::default::Font::from_handle(handle) { + Ok(font) => loaded.push(font), + Err(err) => { + log::warn!("Failed to load font '{}': {}", name, err); + return None; + } + } + } + let properties = loaded.iter().map(|f| f.properties()).collect::>(); + let default_index = + find_best_match(&properties, &font_kit::properties::Properties::new()).unwrap_or(0); + let font_family_name = + loaded.first().map(|f| f.family_name()).unwrap_or_else(|| name.to_string()); + Some(LoadedFontFamily { + family_name: font_family_name, + fonts: loaded, + handles, + properties, + default_index, + }) +} + +pub fn load_font(handle: &font_kit::handle::Handle) -> Result { + let loaded = font_kit::loaders::default::Font::from_handle(handle)?; + let data = match handle { + font_kit::handle::Handle::Memory { bytes, font_index } => egui::FontData { + font: Cow::Owned(bytes.to_vec()), + index: *font_index, + tweak: Default::default(), + }, + font_kit::handle::Handle::Path { path, font_index } => { + let vec = fs::read(path).with_context(|| { + format!("Failed to load font '{}' (index {})", path.display(), font_index) + })?; + egui::FontData { font: Cow::Owned(vec), index: *font_index, tweak: Default::default() } + } + }; + Ok(LoadedFont { font_name: loaded.full_name(), font_data: data }) +} + +pub fn load_font_if_needed( + ctx: &egui::Context, + source: &font_kit::source::SystemSource, + font_id: &egui::FontId, + base_family: egui::FontFamily, + fonts: &mut egui::FontDefinitions, +) -> Result<()> { + if fonts.families.contains_key(&font_id.family) { + return Ok(()); + } + let family_name = match &font_id.family { + egui::FontFamily::Proportional | egui::FontFamily::Monospace => return Ok(()), + egui::FontFamily::Name(v) => v, + }; + let family = load_font_family(source, family_name) + .with_context(|| format!("Failed to load font family '{}'", family_name))?; + let default_fonts = fonts.families.get(&base_family).cloned().unwrap_or_default(); + // FIXME clean up + let default_font_ref = family.fonts.get(family.default_index).unwrap(); + let default_font = family.handles.get(family.default_index).unwrap(); + let default_font_data = load_font(default_font).unwrap(); + log::info!("Loaded font family '{}'", family.family_name); + fonts.font_data.insert(default_font_ref.full_name(), default_font_data.font_data); + fonts + .families + .entry(egui::FontFamily::Name(Arc::from(family.family_name))) + .or_insert_with(|| default_fonts) + .insert(0, default_font_ref.full_name()); + ctx.set_fonts(fonts.clone()); + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index b332c3e..eab0251 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ mod app; mod app_config; mod config; mod diff; +mod fonts; mod jobs; mod obj; mod update; diff --git a/src/views/appearance.rs b/src/views/appearance.rs index cf12d6c..c7f8623 100644 --- a/src/views/appearance.rs +++ b/src/views/appearance.rs @@ -1,6 +1,10 @@ -use egui::{Color32, FontFamily, FontId, TextStyle}; +use std::sync::Arc; + +use egui::{text::LayoutJob, Color32, FontFamily, FontId, TextStyle, Widget}; use time::UtcOffset; +use crate::fonts::load_font_if_needed; + #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct Appearance { @@ -28,13 +32,29 @@ pub struct Appearance { // Global #[serde(skip)] pub utc_offset: UtcOffset, + #[serde(skip)] + pub fonts: FontState, + #[serde(skip)] + pub next_ui_font: Option, + #[serde(skip)] + pub next_code_font: Option, } +pub struct FontState { + definitions: egui::FontDefinitions, + source: font_kit::source::SystemSource, + family_names: Vec, + // loaded_families: HashMap, +} + +const DEFAULT_UI_FONT: FontId = FontId { size: 12.0, family: FontFamily::Proportional }; +const DEFAULT_CODE_FONT: FontId = FontId { size: 14.0, family: FontFamily::Monospace }; + impl Default for Appearance { fn default() -> Self { Self { - ui_font: FontId { size: 12.0, family: FontFamily::Proportional }, - code_font: FontId { size: 14.0, family: FontFamily::Monospace }, + ui_font: DEFAULT_UI_FONT, + code_font: DEFAULT_CODE_FONT, diff_colors: DEFAULT_COLOR_ROTATION.to_vec(), theme: eframe::Theme::Dark, text_color: Color32::GRAY, @@ -45,13 +65,27 @@ impl Default for Appearance { insert_color: Color32::GREEN, delete_color: Color32::from_rgb(200, 40, 41), utc_offset: UtcOffset::UTC, + fonts: FontState::default(), + next_ui_font: None, + next_code_font: None, + } + } +} + +impl Default for FontState { + fn default() -> Self { + Self { + definitions: Default::default(), + source: font_kit::source::SystemSource::new(), + family_names: Default::default(), + // loaded_families: Default::default(), } } } impl Appearance { - pub fn apply(&mut self, style: &egui::Style) -> egui::Style { - let mut style = style.clone(); + pub fn pre_update(&mut self, ctx: &egui::Context) { + let mut style = ctx.style().as_ref().clone(); style.text_styles.insert(TextStyle::Body, FontId { size: (self.ui_font.size * 0.75).floor(), family: self.ui_font.family.clone(), @@ -85,7 +119,71 @@ impl Appearance { self.delete_color = Color32::from_rgb(200, 40, 41); } } - style + ctx.set_style(style); + } + + pub fn post_update(&mut self, ctx: &egui::Context) { + // Load fonts for next frame + if let Some(next_ui_font) = self.next_ui_font.take() { + match load_font_if_needed( + ctx, + &self.fonts.source, + &next_ui_font, + DEFAULT_UI_FONT.family, + &mut self.fonts.definitions, + ) { + Ok(()) => self.ui_font = next_ui_font, + Err(e) => { + log::error!("Failed to load font: {}", e) + } + } + } + if let Some(next_code_font) = self.next_code_font.take() { + match load_font_if_needed( + ctx, + &self.fonts.source, + &next_code_font, + DEFAULT_CODE_FONT.family, + &mut self.fonts.definitions, + ) { + Ok(()) => self.code_font = next_code_font, + Err(e) => { + log::error!("Failed to load font: {}", e) + } + } + } + } + + pub fn init_fonts(&mut self, ctx: &egui::Context) { + self.fonts.family_names = self.fonts.source.all_families().unwrap_or_default(); + match load_font_if_needed( + ctx, + &self.fonts.source, + &self.ui_font, + DEFAULT_UI_FONT.family, + &mut self.fonts.definitions, + ) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to load font: {}", e); + // Revert to default + self.ui_font = DEFAULT_UI_FONT; + } + } + match load_font_if_needed( + ctx, + &self.fonts.source, + &self.code_font, + DEFAULT_CODE_FONT.family, + &mut self.fonts.definitions, + ) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to load font: {}", e); + // Revert to default + self.code_font = DEFAULT_CODE_FONT; + } + } } } @@ -101,6 +199,65 @@ pub const DEFAULT_COLOR_ROTATION: [Color32; 9] = [ Color32::from_rgb(213, 138, 138), ]; +fn font_id_ui( + ui: &mut egui::Ui, + label: &str, + mut font_id: FontId, + default: FontId, + appearance: &Appearance, +) -> Option { + ui.push_id(label, |ui| { + let font_size = font_id.size; + let label_job = LayoutJob::simple( + font_id.family.to_string(), + font_id.clone(), + appearance.text_color, + 0.0, + ); + let mut changed = ui + .horizontal(|ui| { + ui.label(label); + let mut changed = egui::Slider::new(&mut font_id.size, 4.0..=40.0) + .max_decimals(1) + .ui(ui) + .changed(); + if ui.button("Reset").clicked() { + font_id = default; + changed = true; + } + changed + }) + .inner; + let family = &mut font_id.family; + changed |= egui::ComboBox::from_label("Font family") + .selected_text(label_job) + .width(font_size * 20.0) + .show_ui(ui, |ui| { + let mut result = false; + result |= ui + .selectable_value(family, FontFamily::Proportional, "Proportional (built-in)") + .changed(); + result |= ui + .selectable_value(family, FontFamily::Monospace, "Monospace (built-in)") + .changed(); + for family_name in &appearance.fonts.family_names { + result |= ui + .selectable_value( + family, + FontFamily::Name(Arc::from(family_name.as_str())), + family_name, + ) + .changed(); + } + result + }) + .inner + .unwrap_or(false); + changed.then_some(font_id) + }) + .inner +} + pub fn appearance_window(ctx: &egui::Context, show: &mut bool, appearance: &mut Appearance) { egui::Window::new("Appearance").open(show).show(ctx, |ui| { egui::ComboBox::from_label("Theme") @@ -109,11 +266,17 @@ pub fn appearance_window(ctx: &egui::Context, show: &mut bool, appearance: &mut ui.selectable_value(&mut appearance.theme, eframe::Theme::Dark, "Dark"); ui.selectable_value(&mut appearance.theme, eframe::Theme::Light, "Light"); }); - ui.label("UI font:"); - egui::introspection::font_id_ui(ui, &mut appearance.ui_font); ui.separator(); - ui.label("Code font:"); - egui::introspection::font_id_ui(ui, &mut appearance.code_font); + appearance.next_ui_font = + font_id_ui(ui, "UI font:", appearance.ui_font.clone(), DEFAULT_UI_FONT, appearance); + ui.separator(); + appearance.next_code_font = font_id_ui( + ui, + "Code font:", + appearance.code_font.clone(), + DEFAULT_CODE_FONT, + appearance, + ); ui.separator(); ui.label("Diff colors:"); if ui.button("Reset").clicked() { diff --git a/src/views/function_diff.rs b/src/views/function_diff.rs index 2c68b23..2433269 100644 --- a/src/views/function_diff.rs +++ b/src/views/function_diff.rs @@ -4,8 +4,7 @@ use std::{ }; use cwdemangle::demangle; -use eframe::emath::Align; -use egui::{text::LayoutJob, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2}; +use egui::{text::LayoutJob, Align, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2}; use egui_extras::{Column, TableBuilder, TableRow}; use ppc750cl::Argument; use time::format_description;