mirror of https://github.com/encounter/objdiff.git
Add font loading & configuration
This commit is contained in:
parent
879e03eed5
commit
e1079db93a
|
@ -707,6 +707,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cocoa"
|
name = "cocoa"
|
||||||
version = "0.24.1"
|
version = "0.24.1"
|
||||||
|
@ -801,6 +810,12 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-cstr"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.32"
|
version = "0.2.32"
|
||||||
|
@ -861,6 +876,18 @@ dependencies = [
|
||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
@ -977,6 +1004,16 @@ dependencies = [
|
||||||
"dirs-sys",
|
"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]]
|
[[package]]
|
||||||
name = "dirs-sys"
|
name = "dirs-sys"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -1021,6 +1058,18 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
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]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.23.0"
|
version = "0.23.0"
|
||||||
|
@ -1374,12 +1423,49 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"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]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
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]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -1431,6 +1517,27 @@ dependencies = [
|
||||||
"percent-encoding",
|
"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]]
|
[[package]]
|
||||||
name = "fsevent-sys"
|
name = "fsevent-sys"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
|
@ -2674,6 +2781,8 @@ dependencies = [
|
||||||
"exec",
|
"exec",
|
||||||
"filetime",
|
"filetime",
|
||||||
"flagset",
|
"flagset",
|
||||||
|
"float-ord 0.3.2",
|
||||||
|
"font-kit",
|
||||||
"globset",
|
"globset",
|
||||||
"log",
|
"log",
|
||||||
"memmap2 0.9.0",
|
"memmap2 0.9.0",
|
||||||
|
@ -2857,6 +2966,25 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
|
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]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
|
@ -3232,6 +3360,15 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
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]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.37.27"
|
version = "0.37.27"
|
||||||
|
@ -4768,6 +4905,15 @@ dependencies = [
|
||||||
"toml 0.5.11",
|
"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]]
|
[[package]]
|
||||||
name = "x11-dl"
|
name = "x11-dl"
|
||||||
version = "2.21.0"
|
version = "2.21.0"
|
||||||
|
@ -4826,6 +4972,18 @@ version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
|
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]]
|
[[package]]
|
||||||
name = "zbus"
|
name = "zbus"
|
||||||
version = "3.14.1"
|
version = "3.14.1"
|
||||||
|
|
|
@ -36,6 +36,8 @@ egui = "0.23.0"
|
||||||
egui_extras = "0.23.0"
|
egui_extras = "0.23.0"
|
||||||
filetime = "0.2.22"
|
filetime = "0.2.22"
|
||||||
flagset = "0.4.4"
|
flagset = "0.4.4"
|
||||||
|
float-ord = "0.3.2"
|
||||||
|
font-kit = "0.11.0"
|
||||||
globset = { version = "0.4.13", features = ["serde1"] }
|
globset = { version = "0.4.13", features = ["serde1"] }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
memmap2 = "0.9.0"
|
memmap2 = "0.9.0"
|
||||||
|
|
14
src/app.rs
14
src/app.rs
|
@ -223,9 +223,6 @@ impl App {
|
||||||
utc_offset: UtcOffset,
|
utc_offset: UtcOffset,
|
||||||
relaunch_path: Rc<Mutex<Option<PathBuf>>>,
|
relaunch_path: Rc<Mutex<Option<PathBuf>>>,
|
||||||
) -> Self {
|
) -> 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).
|
// Load previous app state (if any).
|
||||||
// Note that you must enable the `persistence` feature for this to work.
|
// Note that you must enable the `persistence` feature for this to work.
|
||||||
let mut app = Self::default();
|
let mut app = Self::default();
|
||||||
|
@ -245,12 +242,15 @@ impl App {
|
||||||
app.config = Arc::new(RwLock::new(config));
|
app.config = Arc::new(RwLock::new(config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
app.appearance.init_fonts(&cc.egui_ctx);
|
||||||
app.appearance.utc_offset = utc_offset;
|
app.appearance.utc_offset = utc_offset;
|
||||||
app.relaunch_path = relaunch_path;
|
app.relaunch_path = relaunch_path;
|
||||||
app
|
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 ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state;
|
||||||
|
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
|
@ -306,6 +306,8 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_update(&mut self, ctx: &egui::Context) {
|
fn post_update(&mut self, ctx: &egui::Context) {
|
||||||
|
self.appearance.post_update(ctx);
|
||||||
|
|
||||||
let ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state;
|
let ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state;
|
||||||
config_state.post_update(ctx, jobs, &self.config);
|
config_state.post_update(ctx, jobs, &self.config);
|
||||||
diff_state.post_update(&self.config);
|
diff_state.post_update(&self.config);
|
||||||
|
@ -400,11 +402,9 @@ impl eframe::App for App {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pre_update();
|
self.pre_update(ctx);
|
||||||
|
|
||||||
let Self { config, appearance, view_state, .. } = self;
|
let Self { config, appearance, view_state, .. } = self;
|
||||||
ctx.set_style(appearance.apply(ctx.style().as_ref()));
|
|
||||||
|
|
||||||
let ViewState {
|
let ViewState {
|
||||||
jobs,
|
jobs,
|
||||||
config_state,
|
config_state,
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
// font-kit/src/matching.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2018 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<usize, SelectionError> {
|
||||||
|
// Step 4.
|
||||||
|
let mut matching_set: Vec<usize> = (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)
|
||||||
|
}
|
|
@ -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<font_kit::font::Font>,
|
||||||
|
pub handles: Vec<font_kit::handle::Handle>,
|
||||||
|
pub properties: Vec<font_kit::properties::Properties>,
|
||||||
|
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<LoadedFontFamily> {
|
||||||
|
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::<Vec<_>>();
|
||||||
|
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<LoadedFont> {
|
||||||
|
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(())
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ mod app;
|
||||||
mod app_config;
|
mod app_config;
|
||||||
mod config;
|
mod config;
|
||||||
mod diff;
|
mod diff;
|
||||||
|
mod fonts;
|
||||||
mod jobs;
|
mod jobs;
|
||||||
mod obj;
|
mod obj;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
|
@ -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 time::UtcOffset;
|
||||||
|
|
||||||
|
use crate::fonts::load_font_if_needed;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Appearance {
|
pub struct Appearance {
|
||||||
|
@ -28,13 +32,29 @@ pub struct Appearance {
|
||||||
// Global
|
// Global
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub utc_offset: UtcOffset,
|
pub utc_offset: UtcOffset,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub fonts: FontState,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub next_ui_font: Option<FontId>,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub next_code_font: Option<FontId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FontState {
|
||||||
|
definitions: egui::FontDefinitions,
|
||||||
|
source: font_kit::source::SystemSource,
|
||||||
|
family_names: Vec<String>,
|
||||||
|
// loaded_families: HashMap<String, LoadedFontFamily>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
impl Default for Appearance {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ui_font: FontId { size: 12.0, family: FontFamily::Proportional },
|
ui_font: DEFAULT_UI_FONT,
|
||||||
code_font: FontId { size: 14.0, family: FontFamily::Monospace },
|
code_font: DEFAULT_CODE_FONT,
|
||||||
diff_colors: DEFAULT_COLOR_ROTATION.to_vec(),
|
diff_colors: DEFAULT_COLOR_ROTATION.to_vec(),
|
||||||
theme: eframe::Theme::Dark,
|
theme: eframe::Theme::Dark,
|
||||||
text_color: Color32::GRAY,
|
text_color: Color32::GRAY,
|
||||||
|
@ -45,13 +65,27 @@ impl Default for Appearance {
|
||||||
insert_color: Color32::GREEN,
|
insert_color: Color32::GREEN,
|
||||||
delete_color: Color32::from_rgb(200, 40, 41),
|
delete_color: Color32::from_rgb(200, 40, 41),
|
||||||
utc_offset: UtcOffset::UTC,
|
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 {
|
impl Appearance {
|
||||||
pub fn apply(&mut self, style: &egui::Style) -> egui::Style {
|
pub fn pre_update(&mut self, ctx: &egui::Context) {
|
||||||
let mut style = style.clone();
|
let mut style = ctx.style().as_ref().clone();
|
||||||
style.text_styles.insert(TextStyle::Body, FontId {
|
style.text_styles.insert(TextStyle::Body, FontId {
|
||||||
size: (self.ui_font.size * 0.75).floor(),
|
size: (self.ui_font.size * 0.75).floor(),
|
||||||
family: self.ui_font.family.clone(),
|
family: self.ui_font.family.clone(),
|
||||||
|
@ -85,7 +119,71 @@ impl Appearance {
|
||||||
self.delete_color = Color32::from_rgb(200, 40, 41);
|
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),
|
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<FontId> {
|
||||||
|
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) {
|
pub fn appearance_window(ctx: &egui::Context, show: &mut bool, appearance: &mut Appearance) {
|
||||||
egui::Window::new("Appearance").open(show).show(ctx, |ui| {
|
egui::Window::new("Appearance").open(show).show(ctx, |ui| {
|
||||||
egui::ComboBox::from_label("Theme")
|
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::Dark, "Dark");
|
||||||
ui.selectable_value(&mut appearance.theme, eframe::Theme::Light, "Light");
|
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.separator();
|
||||||
ui.label("Code font:");
|
appearance.next_ui_font =
|
||||||
egui::introspection::font_id_ui(ui, &mut appearance.code_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.separator();
|
||||||
ui.label("Diff colors:");
|
ui.label("Diff colors:");
|
||||||
if ui.button("Reset").clicked() {
|
if ui.button("Reset").clicked() {
|
||||||
|
|
|
@ -4,8 +4,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use cwdemangle::demangle;
|
use cwdemangle::demangle;
|
||||||
use eframe::emath::Align;
|
use egui::{text::LayoutJob, Align, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2};
|
||||||
use egui::{text::LayoutJob, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2};
|
|
||||||
use egui_extras::{Column, TableBuilder, TableRow};
|
use egui_extras::{Column, TableBuilder, TableRow};
|
||||||
use ppc750cl::Argument;
|
use ppc750cl::Argument;
|
||||||
use time::format_description;
|
use time::format_description;
|
||||||
|
|
Loading…
Reference in New Issue