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"
|
enumset = "1.1.10"
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
flate2 = "1.1.4"
|
flate2 = "1.1.4"
|
||||||
|
fontdb = "0.24.0"
|
||||||
fs4 = { version = "0.13.1", default-features = false }
|
fs4 = { version = "0.13.1", default-features = false }
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
futures-lite = "2.6.1"
|
futures-lite = "2.6.1"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ dashmap = { workspace = true }
|
|||||||
either = { workspace = true }
|
either = { workspace = true }
|
||||||
enumset = { workspace = true, features = ["serde"] }
|
enumset = { workspace = true, features = ["serde"] }
|
||||||
flate2 = { workspace = true }
|
flate2 = { workspace = true }
|
||||||
|
fontdb = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
hyper = { workspace = true, features = ["server"] }
|
hyper = { workspace = true, features = ["server"] }
|
||||||
hyper-util = { workspace = true }
|
hyper-util = { workspace = true }
|
||||||
|
|||||||
@@ -697,6 +697,14 @@ fn main() {
|
|||||||
DefaultPermissionRule::AllowAllCommands,
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.plugin(
|
||||||
|
"fonts",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&["fonts_get_system_fonts"])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
.plugin(
|
.plugin(
|
||||||
"drop",
|
"drop",
|
||||||
InlinedPlugin::new()
|
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 datapacks;
|
||||||
pub mod drop;
|
pub mod drop;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
|
pub mod fonts;
|
||||||
pub mod friends;
|
pub mod friends;
|
||||||
pub mod worlds;
|
pub mod worlds;
|
||||||
|
|
||||||
|
|||||||
@@ -947,6 +947,7 @@ fn main() {
|
|||||||
.plugin(api::datapacks::init())
|
.plugin(api::datapacks::init())
|
||||||
.plugin(api::drop::init())
|
.plugin(api::drop::init())
|
||||||
.plugin(api::files::init())
|
.plugin(api::files::init())
|
||||||
|
.plugin(api::fonts::init())
|
||||||
.plugin(api::friends::init())
|
.plugin(api::friends::init())
|
||||||
.plugin(api::worlds::init())
|
.plugin(api::worlds::init())
|
||||||
.plugin(api::terracotta::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")]
|
#[serde(default = "default_ui_scale")]
|
||||||
pub ui_scale: f64,
|
pub ui_scale: f64,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub ui_font: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub mono_font: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub close_behavior: String,
|
pub close_behavior: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub auto_hide_downloads_button: bool,
|
pub auto_hide_downloads_button: bool,
|
||||||
@@ -289,6 +293,8 @@ impl Settings {
|
|||||||
json(home_widgets) as \"home_widgets?: String\", auto_hide_downloads_button,
|
json(home_widgets) as \"home_widgets?: String\", auto_hide_downloads_button,
|
||||||
json(terracotta_public_nodes) terracotta_public_nodes,
|
json(terracotta_public_nodes) terracotta_public_nodes,
|
||||||
ui_scale,
|
ui_scale,
|
||||||
|
ui_font,
|
||||||
|
mono_font,
|
||||||
version
|
version
|
||||||
FROM settings
|
FROM settings
|
||||||
"
|
"
|
||||||
@@ -362,6 +368,8 @@ impl Settings {
|
|||||||
transparent_background_blur: res.transparent_background_blur == 1,
|
transparent_background_blur: res.transparent_background_blur == 1,
|
||||||
sidebar_instance_count: res.sidebar_instance_count as u32,
|
sidebar_instance_count: res.sidebar_instance_count as u32,
|
||||||
ui_scale: res.ui_scale,
|
ui_scale: res.ui_scale,
|
||||||
|
ui_font: res.ui_font,
|
||||||
|
mono_font: res.mono_font,
|
||||||
close_behavior,
|
close_behavior,
|
||||||
auto_hide_downloads_button: res.auto_hide_downloads_button == 1,
|
auto_hide_downloads_button: res.auto_hide_downloads_button == 1,
|
||||||
home_layout: HomeLayout::from_string(&res.home_layout),
|
home_layout: HomeLayout::from_string(&res.home_layout),
|
||||||
@@ -665,6 +673,14 @@ impl Settings {
|
|||||||
.bind(self.ui_scale)
|
.bind(self.ui_scale)
|
||||||
.execute(exec)
|
.execute(exec)
|
||||||
.await?;
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user