feat(fonts): 后端字体命令 fonts_get_system_fonts 及 ui_font/mono_font 设置字段
This commit is contained in:
1 parent
ead942abb2
commit
b57219f348
8 files changed
+121
No files matched your search
@@ -44,6 +44,7 @@ encoding_rs = "0.8.35"
|
||||
enumset = "1.1.10"
|
||||
eyre = "0.6.12"
|
||||
flate2 = "1.1.4"
|
||||
fontdb = "0.24.0"
|
||||
fs4 = { version = "0.13.1", default-features = false }
|
||||
futures = "0.3.31"
|
||||
futures-lite = "2.6.1"
|
||||
|
||||
@@ -16,6 +16,7 @@ dashmap = { workspace = true }
|
||||
either = { workspace = true }
|
||||
enumset = { workspace = true, features = ["serde"] }
|
||||
flate2 = { workspace = true }
|
||||
fontdb = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hyper = { workspace = true, features = ["server"] }
|
||||
hyper-util = { workspace = true }
|
||||
|
||||
@@ -697,6 +697,14 @@ fn main() {
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"fonts",
|
||||
InlinedPlugin::new()
|
||||
.commands(&["fonts_get_system_fonts"])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"drop",
|
||||
InlinedPlugin::new()
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::api::Result;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SystemFontFamily {
|
||||
pub family: String,
|
||||
pub monospaced: bool,
|
||||
}
|
||||
|
||||
/// Scanning every installed face costs a few hundred milliseconds, and the
|
||||
/// collection only changes between sessions, so it is read once per process.
|
||||
static SYSTEM_FONTS: OnceLock<Vec<SystemFontFamily>> = OnceLock::new();
|
||||
|
||||
type FontFamilies = [(String, fontdb::Language)];
|
||||
|
||||
/// fontdb lists the English US family first when the font has one, but the
|
||||
/// lookup is explicit so a face with an unusual name table still resolves to
|
||||
/// the name the webview matches against.
|
||||
fn english_family_name(families: &FontFamilies) -> Option<&str> {
|
||||
families
|
||||
.iter()
|
||||
.find(|(_, language)| {
|
||||
*language == fontdb::Language::English_UnitedStates
|
||||
})
|
||||
.or_else(|| families.first())
|
||||
.map(|(name, _)| name.trim())
|
||||
.filter(|name| !name.is_empty())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FamilyAccumulator {
|
||||
families: HashMap<String, bool>,
|
||||
}
|
||||
|
||||
impl FamilyAccumulator {
|
||||
fn add(&mut self, families: &FontFamilies, monospaced: bool) {
|
||||
let Some(name) = english_family_name(families) else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.families
|
||||
.entry(name.to_owned())
|
||||
.and_modify(|flag| *flag |= monospaced)
|
||||
.or_insert(monospaced);
|
||||
}
|
||||
|
||||
fn into_sorted_families(self) -> Vec<SystemFontFamily> {
|
||||
let mut families: Vec<SystemFontFamily> = self
|
||||
.families
|
||||
.into_iter()
|
||||
.map(|(family, monospaced)| SystemFontFamily { family, monospaced })
|
||||
.collect();
|
||||
families.sort_by_cached_key(|entry| entry.family.to_lowercase());
|
||||
families
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_system_fonts() -> Vec<SystemFontFamily> {
|
||||
let mut database = fontdb::Database::new();
|
||||
database.load_system_fonts();
|
||||
|
||||
let mut accumulator = FamilyAccumulator::default();
|
||||
for face in database.faces() {
|
||||
accumulator.add(&face.families, face.monospaced);
|
||||
}
|
||||
|
||||
accumulator.into_sorted_families()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn fonts_get_system_fonts() -> Result<Vec<SystemFontFamily>> {
|
||||
if let Some(fonts) = SYSTEM_FONTS.get() {
|
||||
return Ok(fonts.clone());
|
||||
}
|
||||
|
||||
let fonts = tauri::async_runtime::spawn_blocking(collect_system_fonts)
|
||||
.await
|
||||
.map_err(std::io::Error::other)?;
|
||||
|
||||
// A concurrent caller may have won the race; the scan is deterministic.
|
||||
Ok(SYSTEM_FONTS.get_or_init(|| fonts).clone())
|
||||
}
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("fonts")
|
||||
.invoke_handler(tauri::generate_handler![fonts_get_system_fonts])
|
||||
.build()
|
||||
}
|
||||
@@ -36,6 +36,7 @@ pub mod curseforge;
|
||||
pub mod datapacks;
|
||||
pub mod drop;
|
||||
pub mod files;
|
||||
pub mod fonts;
|
||||
pub mod friends;
|
||||
pub mod worlds;
|
||||
|
||||
|
||||
@@ -947,6 +947,7 @@ fn main() {
|
||||
.plugin(api::datapacks::init())
|
||||
.plugin(api::drop::init())
|
||||
.plugin(api::files::init())
|
||||
.plugin(api::fonts::init())
|
||||
.plugin(api::friends::init())
|
||||
.plugin(api::worlds::init())
|
||||
.plugin(api::terracotta::init())
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE settings ADD COLUMN ui_font TEXT;
|
||||
ALTER TABLE settings ADD COLUMN mono_font TEXT;
|
||||
@@ -145,6 +145,10 @@ pub struct Settings {
|
||||
#[serde(default = "default_ui_scale")]
|
||||
pub ui_scale: f64,
|
||||
#[serde(default)]
|
||||
pub ui_font: Option<String>,
|
||||
#[serde(default)]
|
||||
pub mono_font: Option<String>,
|
||||
#[serde(default)]
|
||||
pub close_behavior: String,
|
||||
#[serde(default)]
|
||||
pub auto_hide_downloads_button: bool,
|
||||
@@ -289,6 +293,8 @@ impl Settings {
|
||||
json(home_widgets) as \"home_widgets?: String\", auto_hide_downloads_button,
|
||||
json(terracotta_public_nodes) terracotta_public_nodes,
|
||||
ui_scale,
|
||||
ui_font,
|
||||
mono_font,
|
||||
version
|
||||
FROM settings
|
||||
"
|
||||
@@ -362,6 +368,8 @@ impl Settings {
|
||||
transparent_background_blur: res.transparent_background_blur == 1,
|
||||
sidebar_instance_count: res.sidebar_instance_count as u32,
|
||||
ui_scale: res.ui_scale,
|
||||
ui_font: res.ui_font,
|
||||
mono_font: res.mono_font,
|
||||
close_behavior,
|
||||
auto_hide_downloads_button: res.auto_hide_downloads_button == 1,
|
||||
home_layout: HomeLayout::from_string(&res.home_layout),
|
||||
@@ -665,6 +673,14 @@ impl Settings {
|
||||
.bind(self.ui_scale)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
sqlx::query("UPDATE settings SET ui_font = ? WHERE id = 0")
|
||||
.bind(&self.ui_font)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
sqlx::query("UPDATE settings SET mono_font = ? WHERE id = 0")
|
||||
.bind(&self.mono_font)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user