feat(fonts): 后端字体命令 fonts_get_system_fonts 及 ui_font/mono_font 设置字段

This commit is contained in:
AxTps committed 2026-10-02 21:33:13 +08:00
1 parent ead942abb2
commit b57219f348
8 files changed
+121

No files matched your search

+1
View File
@@ -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"
+1
View File
@@ -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 }
+8
View File
@@ -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()
+91
View File
@@ -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()
}
+1
View File
@@ -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;
+1
View File
@@ -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;
+16
View File
@@ -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(())
}