From d72eaabf5484568626cd3eeeb2fbb5dca5e154e2 Mon Sep 17 00:00:00 2001 From: arabian Date: Wed, 13 May 2026 22:02:24 +0300 Subject: [PATCH] feat: load system fonts --- Cargo.lock | 82 +++++++++++++++++++ Cargo.toml | 1 + packages/flatpak/cargo-sources.json | 117 ++++++++++++++++++++++++++++ src/gui/draw.rs | 19 ++--- src/gui/mod.rs | 57 +++++++++++++- 5 files changed, 261 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc8b1cb..fe7c9d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,6 +722,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + [[package]] name = "coreaudio-rs" version = "0.14.1" @@ -1308,6 +1317,29 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -3027,6 +3059,7 @@ dependencies = [ "rodio", "serde", "serde_json", + "system-fonts", "tokio", ] @@ -3245,6 +3278,12 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -3803,6 +3842,15 @@ dependencies = [ "syn", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-deps" version = "7.0.8" @@ -3816,6 +3864,16 @@ dependencies = [ "version-compare", ] +[[package]] +name = "system-fonts" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f369feb844d5e08bb4e938c3f88ff359261bc94c7553ddb34e174447120b64d6" +dependencies = [ + "fontdb", + "sys-locale", +] + [[package]] name = "tap" version = "1.0.1" @@ -3905,6 +3963,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.52.3" @@ -4016,6 +4089,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + [[package]] name = "type-map" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index bb01f06..7d04a7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ rfd = { version = "0.17.2", default-features = false, features = [ "xdg-portal", ] } opener = { version = "0.8.4", features = ["reveal"] } +system-fonts = "0.1.0" egui = { version = "0.34.2", default-features = false, features = [ "default_fonts", diff --git a/packages/flatpak/cargo-sources.json b/packages/flatpak/cargo-sources.json index 99a6503..a50786c 100644 --- a/packages/flatpak/cargo-sources.json +++ b/packages/flatpak/cargo-sources.json @@ -960,6 +960,19 @@ "dest": "cargo/vendor/core-graphics-types-0.1.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/core_maths/core_maths-0.1.1.crate", + "sha256": "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30", + "dest": "cargo/vendor/core_maths-0.1.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30\", \"files\": {}}", + "dest": "cargo/vendor/core_maths-0.1.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1693,6 +1706,32 @@ "dest": "cargo/vendor/font-types-0.11.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/fontconfig-parser/fontconfig-parser-0.5.8.crate", + "sha256": "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646", + "dest": "cargo/vendor/fontconfig-parser-0.5.8" + }, + { + "type": "inline", + "contents": "{\"package\": \"bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646\", \"files\": {}}", + "dest": "cargo/vendor/fontconfig-parser-0.5.8", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/fontdb/fontdb-0.23.0.crate", + "sha256": "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905", + "dest": "cargo/vendor/fontdb-0.23.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905\", \"files\": {}}", + "dest": "cargo/vendor/fontdb-0.23.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4181,6 +4220,19 @@ "dest": "cargo/vendor/rodio", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/roxmltree/roxmltree-0.20.0.crate", + "sha256": "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97", + "dest": "cargo/vendor/roxmltree-0.20.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97\", \"files\": {}}", + "dest": "cargo/vendor/roxmltree-0.20.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4896,6 +4948,19 @@ "dest": "cargo/vendor/synstructure-0.13.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/sys-locale/sys-locale-0.3.2.crate", + "sha256": "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4", + "dest": "cargo/vendor/sys-locale-0.3.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4\", \"files\": {}}", + "dest": "cargo/vendor/sys-locale-0.3.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4909,6 +4974,19 @@ "dest": "cargo/vendor/system-deps-7.0.8", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/system-fonts/system-fonts-0.1.0.crate", + "sha256": "f369feb844d5e08bb4e938c3f88ff359261bc94c7553ddb34e174447120b64d6", + "dest": "cargo/vendor/system-fonts-0.1.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"f369feb844d5e08bb4e938c3f88ff359261bc94c7553ddb34e174447120b64d6\", \"files\": {}}", + "dest": "cargo/vendor/system-fonts-0.1.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5026,6 +5104,32 @@ "dest": "cargo/vendor/tinystr-0.8.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/tinyvec/tinyvec-1.11.0.crate", + "sha256": "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3", + "dest": "cargo/vendor/tinyvec-1.11.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3\", \"files\": {}}", + "dest": "cargo/vendor/tinyvec-1.11.0", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/tinyvec_macros/tinyvec_macros-0.1.1.crate", + "sha256": "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20", + "dest": "cargo/vendor/tinyvec_macros-0.1.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20\", \"files\": {}}", + "dest": "cargo/vendor/tinyvec_macros-0.1.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5156,6 +5260,19 @@ "dest": "cargo/vendor/tracing-core-0.1.36", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/ttf-parser/ttf-parser-0.25.1.crate", + "sha256": "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31", + "dest": "cargo/vendor/ttf-parser-0.25.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31\", \"files\": {}}", + "dest": "cargo/vendor/ttf-parser-0.25.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", diff --git a/src/gui/draw.rs b/src/gui/draw.rs index 11a0e12..29bcf20 100644 --- a/src/gui/draw.rs +++ b/src/gui/draw.rs @@ -9,10 +9,7 @@ use egui_material_icons::icons::*; use pwsp::types::socket::Request; use pwsp::types::{audio_player::TrackInfo, gui::AppState}; use pwsp::utils::gui::{format_time_pair, make_request_async}; -use std::{ - path::Path, - time::Instant, -}; +use std::{path::Path, time::Instant}; enum TrackAction { Pause(u32), @@ -282,8 +279,7 @@ impl SoundpadGui { row.col(|_| {}); row.col(|ui| { ui.label( - RichText::new("No hotkey slots configured.") - .color(Color32::GRAY), + RichText::new("No hotkey slots configured.").color(Color32::GRAY), ); }); row.col(|_| {}); @@ -305,8 +301,7 @@ impl SoundpadGui { .on_hover_text("Key chord conflict"); } ui.add( - Label::new(RichText::new(&slot.slot).monospace()) - .truncate(), + Label::new(RichText::new(&slot.slot).monospace()).truncate(), ); }); }); @@ -315,9 +310,7 @@ impl SoundpadGui { row.col(|ui| { let action_name = match slot.action.name.as_str() { "play" => { - if let Some(file_path_str) = - slot.action.args.get("file_path") - { + if let Some(file_path_str) = slot.action.args.get("file_path") { Path::new(file_path_str) .file_name() .unwrap_or_default() @@ -334,9 +327,7 @@ impl SoundpadGui { "toggle_loop" => "Toggle Loop".to_string(), other => other.to_string(), }; - ui.add( - Label::new(RichText::new(action_name).monospace()).truncate(), - ); + ui.add(Label::new(RichText::new(action_name).monospace()).truncate()); }); // Column 3: Key Chord diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 3dbcc6f..1393166 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -3,7 +3,7 @@ mod input; mod update; use eframe::{HardwareAcceleration, NativeOptions, icon_data::from_png_bytes, run_native}; -use egui::{Context, Vec2, ViewportBuilder}; +use egui::{Context, FontData, FontDefinitions, FontFamily, FontTweak, Vec2, ViewportBuilder}; use itertools::Itertools; use pwsp::{ types::{ @@ -21,9 +21,11 @@ use pwsp::{ use rfd::FileDialog; use std::{ error::Error, + fs, path::{Path, PathBuf}, sync::{Arc, Mutex}, }; +use system_fonts::{FontStyle, FoundFontSource, find_for_locale}; const SUPPORTED_EXTENSIONS: [&str; 13] = [ "mp3", "wav", "ogg", "flac", "mp4", "m4a", "aac", "mov", "mkv", "mka", "webm", "avi", "opus", @@ -196,6 +198,54 @@ impl SoundpadGui { } } +fn add_font( + font_name: &str, + font_bytes: &[u8], + fonts: &mut FontDefinitions, +) -> Result<(), Box> { + let font_data = FontData::from_owned(font_bytes.to_vec()).tweak(FontTweak { + scale: 1.0, + hinting_override: Some(true), + ..Default::default() + }); + + fonts + .font_data + .insert(font_name.to_owned(), font_data.into()); + + fonts + .families + .entry(FontFamily::Proportional) + .or_default() + .insert(0, font_name.to_owned()); + fonts + .families + .entry(FontFamily::Monospace) + .or_default() + .insert(0, font_name.to_owned()); + + Ok(()) +} + +fn load_system_fonts(fonts: &mut FontDefinitions) -> Result<(), Box> { + let (_, en_sans) = find_for_locale("en", FontStyle::Sans); + let (_, en_serif) = find_for_locale("en", FontStyle::Serif); + let (_, ja_sans) = find_for_locale("ja", FontStyle::Sans); + + let system_fonts = [en_sans, en_serif, ja_sans].concat(); + + for font in system_fonts.iter().rev() { + let font_bytes = match &font.source { + FoundFontSource::Path(path) => fs::read(path)?, + FoundFontSource::Bytes(bytes) => bytes.to_vec(), + }; + + add_font(&font.key, &font_bytes, fonts)?; + } + + Ok(()) +} + pub async fn run() -> Result<(), Box> { const ICON: &[u8] = include_bytes!("../../assets/icon.png"); @@ -218,6 +268,11 @@ pub async fn run() -> Result<(), Box> { options, Box::new(|cc| { egui_material_icons::initialize(&cc.egui_ctx); + + let mut fonts = FontDefinitions::default(); + load_system_fonts(&mut fonts).ok(); + cc.egui_ctx.set_fonts(fonts); + Ok(Box::new(SoundpadGui::new(&cc.egui_ctx))) }), ) {