feat: 支持单独设置中文终端字体
This commit is contained in:
1 parent
ce068600b2
commit
ae66b69e78
10 files changed
+68
-3
No files matched your search
@@ -77,6 +77,9 @@
|
||||
"background_cover_description": "The image will extend beneath the title bar, window buttons, tabs, and SSH sidebar. Low-contrast images may make controls harder to see; the interface retains a minimum-opacity safeguard."
|
||||
},
|
||||
"font": {
|
||||
"english": "English terminal font",
|
||||
"chinese": "Chinese terminal font",
|
||||
"chinese_description": "Used for Chinese and other full-width characters. Enter a font family or comma-separated families; leave empty to use the English font settings. Applies on Enter or leaving the field.",
|
||||
"fallback_description": "The first family is preferred; missing glyphs fall back through the remaining families, with bundled Maple as the final safe fallback.",
|
||||
"group_description": "The first family is primary; the rest are used in fallback order.",
|
||||
"loading": "Enumerating system fonts...",
|
||||
|
||||
@@ -77,6 +77,9 @@
|
||||
"background_cover_description": "背景图会延伸到标题栏、窗口按钮、Tab 与 SSH 侧栏下方,低对比度图片可能影响操作可见性;界面仍会保留最低不透明度保护。"
|
||||
},
|
||||
"font": {
|
||||
"english": "英文终端字体",
|
||||
"chinese": "中文终端字体",
|
||||
"chinese_description": "用于中文等全宽字符。输入字体名称,多个字体以逗号分隔;留空时使用英文字体设置。按回车或离开输入框后生效。",
|
||||
"fallback_description": "第 1 项优先显示;缺失字形依次使用后续字体,内置 Maple 是最终安全兜底。",
|
||||
"group_description": "第 1 项是主字体,其余按顺序回退",
|
||||
"loading": "正在枚举系统字体…",
|
||||
|
||||
@@ -42,6 +42,7 @@ pub struct Settings {
|
||||
/// 已解析的界面语言。GPUI 组件只读这个内存全局,渲染路径不得重复读盘。
|
||||
pub ui_language: UiLanguage,
|
||||
pub font_family: String,
|
||||
pub font_cjk: Option<[gpui::Font; 4]>,
|
||||
pub font_bold_family: String,
|
||||
pub font_italic_family: String,
|
||||
pub font_bold_italic_family: String,
|
||||
@@ -193,6 +194,20 @@ impl Settings {
|
||||
cjk_bold_regular: runtime.cjk_bold_regular,
|
||||
shell_id: runtime.shell.clone(),
|
||||
font_family: normal_family,
|
||||
font_cjk: runtime.font_family_cjk.as_deref().map(|family| {
|
||||
use gpui::{FontStyle, FontWeight};
|
||||
[
|
||||
(FontWeight::NORMAL, FontStyle::Normal),
|
||||
(FontWeight::BOLD, FontStyle::Normal),
|
||||
(FontWeight::NORMAL, FontStyle::Italic),
|
||||
(FontWeight::BOLD, FontStyle::Italic),
|
||||
]
|
||||
.map(|(weight, style)| gpui::Font {
|
||||
weight,
|
||||
style,
|
||||
..crate::font_install::gpui_font_with_fallbacks(family)
|
||||
})
|
||||
}),
|
||||
load_notice,
|
||||
// 这里是唯一能正确合并「主题自带几何」与「用户显式覆盖」的地方:
|
||||
// `theme` 已是 follow_system 折算后的**生效**主题,runtime 是同一次
|
||||
|
||||
@@ -175,6 +175,7 @@ pub struct SettingsPane {
|
||||
/// GPUI text system 已注册的导入族名(启动扫描 + 本次导入累计)。
|
||||
font_imported: Vec<String>,
|
||||
font_family_input: Entity<InputState>,
|
||||
font_family_cjk_input: Entity<InputState>,
|
||||
/// 字体输入框上一帧的窗口坐标。字体目录是宽弹层,不能把整条设置行当
|
||||
/// 锚点;否则输入框在右侧、菜单却会从正文左缘展开。
|
||||
font_picker_trigger_bounds: Option<gpui::Bounds<gpui::Pixels>>,
|
||||
|
||||
@@ -96,11 +96,19 @@ impl SettingsPane {
|
||||
let terminal = self
|
||||
.group(language.pick("终端外观", "Terminal appearance"), cx)
|
||||
.child(self.row(
|
||||
language.pick("终端字体", "Terminal font"),
|
||||
language.text(crate::i18n::Message::SettingsFontEnglish),
|
||||
help("font_family", language),
|
||||
font_picker,
|
||||
cx,
|
||||
))
|
||||
.child(self.row(
|
||||
language.text(crate::i18n::Message::SettingsFontChinese),
|
||||
language.text(crate::i18n::Message::SettingsFontChineseDescription),
|
||||
Input::new(&self.font_family_cjk_input)
|
||||
.w(px(SETTINGS_SELECT_WIDTH))
|
||||
.aria_label(language.text(crate::i18n::Message::SettingsFontChinese)),
|
||||
cx,
|
||||
))
|
||||
.child(self.terminal_font_size_row(cx))
|
||||
.child(self.select_row(
|
||||
"cell_width_mode",
|
||||
|
||||
@@ -219,7 +219,7 @@ impl SettingsPane {
|
||||
cx.stop_propagation();
|
||||
cx.notify();
|
||||
})))
|
||||
.aria_label(language.pick("终端字体", "Terminal font")),
|
||||
.aria_label(language.text(crate::i18n::Message::SettingsFontEnglish)),
|
||||
)
|
||||
// 弹层仍须取输入框的真实窗口坐标,才能正确处理滚动、缩放与 DPI。
|
||||
.child(
|
||||
@@ -246,7 +246,7 @@ impl SettingsPane {
|
||||
return control.into_any_element();
|
||||
}
|
||||
self.row(
|
||||
language.pick("终端字体", "Terminal font"),
|
||||
language.text(crate::i18n::Message::SettingsFontEnglish),
|
||||
help("font_family", language),
|
||||
control,
|
||||
cx,
|
||||
|
||||
@@ -439,6 +439,28 @@ impl SettingsPane {
|
||||
},
|
||||
));
|
||||
|
||||
let font_family_cjk_input = cx.new(|cx| {
|
||||
InputState::new(window, cx)
|
||||
.default_value(runtime.font_family_cjk.clone().unwrap_or_default())
|
||||
});
|
||||
subscriptions.push(cx.subscribe_in(
|
||||
&font_family_cjk_input,
|
||||
window,
|
||||
|this: &mut Self, _, event: &InputEvent, window, cx| {
|
||||
if matches!(event, InputEvent::PressEnter { .. } | InputEvent::Blur) {
|
||||
let value = crate::font_install::normalize_font_family_chain(
|
||||
&this.font_family_cjk_input.read(cx).value(),
|
||||
);
|
||||
this.font_family_cjk_input.update(cx, |input, cx| {
|
||||
input.set_value(value.clone(), window, cx);
|
||||
});
|
||||
if this.runtime.font_family_cjk.as_deref().unwrap_or_default() != value {
|
||||
this.persist(&[("font_family_cjk", value)], cx);
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
let bg_picker_hsv = {
|
||||
let term = crate::gpui_shell::theme::chrome_theme_resolved(cx).palette().term_bg;
|
||||
let rgb = runtime.background.unwrap_or([term.r, term.g, term.b]);
|
||||
@@ -548,6 +570,7 @@ impl SettingsPane {
|
||||
font_system: None,
|
||||
font_imported: Vec::new(),
|
||||
font_family_input,
|
||||
font_family_cjk_input,
|
||||
font_picker_trigger_bounds: None,
|
||||
backup_selection: crate::encrypted_backup::BackupSelection::default(),
|
||||
backup_pass_input: cx.new(|cx| {
|
||||
|
||||
@@ -497,8 +497,14 @@ impl Element for TerminalElement {
|
||||
.try_global::<crate::gpui_shell::config::Settings>()
|
||||
.map(|settings| settings.cjk_bold_regular)
|
||||
.unwrap_or(true);
|
||||
let cjk_fonts = cx
|
||||
.try_global::<crate::gpui_shell::config::Settings>()
|
||||
.and_then(|settings| settings.font_cjk.clone());
|
||||
let pick_font = |bold: bool, italic: bool, wide: bool| {
|
||||
let bold = bold && !(wide && cjk_bold_regular);
|
||||
if let Some(fonts) = cjk_fonts.as_ref().filter(|_| wide) {
|
||||
return fonts[usize::from(bold) + 2 * usize::from(italic)].clone();
|
||||
}
|
||||
match (bold, italic) {
|
||||
(false, false) => font.clone(),
|
||||
(true, false) => bold_font.clone(),
|
||||
|
||||
@@ -941,6 +941,7 @@ pub struct RuntimeSettings {
|
||||
/// 系统外观变化时自动在主题家族的深浅成员间切换(默认关,尊重显式选择)。
|
||||
pub follow_system_theme: bool,
|
||||
pub font_family: Option<String>,
|
||||
pub font_family_cjk: Option<String>,
|
||||
/// **逻辑像素**(旧壳写盘语义:设置页 spinner 与 Ctrl+滚轮缩放持久化时
|
||||
/// 已除以 scale factor)。`None` = 跟随 nebula.toml 的 `font.size`(pt)。
|
||||
pub font_size_px: Option<f32>,
|
||||
@@ -1081,6 +1082,7 @@ impl RuntimeSettings {
|
||||
.unwrap_or_default(),
|
||||
follow_system_theme: raw.bool_on("follow_system_theme").unwrap_or(false),
|
||||
font_family: raw.value("font_family").map(str::to_owned),
|
||||
font_family_cjk: raw.value("font_family_cjk").map(str::to_owned),
|
||||
font_size_px: raw.f32("font_size").map(|size| size.clamp(4.0, 96.0)),
|
||||
cursor_shape: raw.value("cursor_shape").and_then(CursorShapeName::from_settings),
|
||||
cursor_blink: raw.bool_on("cursor_blink"),
|
||||
@@ -1305,6 +1307,7 @@ mod tests {
|
||||
completion_style=popup\n\
|
||||
shell=pwsh\n\
|
||||
font_family=Maple Mono Normal NF CN\n\
|
||||
font_family_cjk=PingFang SC\n\
|
||||
font_size=16.3\n\
|
||||
cursor_shape=beam\n\
|
||||
cursor_blink=1\n\
|
||||
@@ -1339,6 +1342,7 @@ mod tests {
|
||||
assert_eq!(settings.language, LanguagePref::ZhCn);
|
||||
assert_eq!(settings.theme, ThemeName::SilverLight);
|
||||
assert_eq!(settings.font_family.as_deref(), Some("Maple Mono Normal NF CN"));
|
||||
assert_eq!(settings.font_family_cjk.as_deref(), Some("PingFang SC"));
|
||||
// font_size 键存的是逻辑像素(旧壳写盘语义),不做 pt 换算。
|
||||
assert_eq!(settings.font_size_px, Some(16.3));
|
||||
assert_eq!(settings.cursor_shape, Some(CursorShapeName::Beam));
|
||||
@@ -1381,6 +1385,7 @@ mod tests {
|
||||
assert_eq!(settings.language, LanguagePref::System);
|
||||
assert_eq!(settings.theme, ThemeName::Nord);
|
||||
assert_eq!(settings.font_family, None);
|
||||
assert_eq!(settings.font_family_cjk, None);
|
||||
assert!(!settings.copy_on_select);
|
||||
assert!(settings.multiline_paste_confirm, "多行粘贴确认默认开(上游兼容)");
|
||||
assert!(settings.tab_close_visible, "标签关闭按钮默认可见(上游兼容)");
|
||||
|
||||
@@ -10,6 +10,7 @@ const RESET_KEYS: &[&str] = &[
|
||||
"app_icon",
|
||||
"follow_system_theme",
|
||||
"font_family",
|
||||
"font_family_cjk",
|
||||
"font_size",
|
||||
"cursor_shape",
|
||||
"cursor_blink",
|
||||
|
||||
Reference in new issue
Block a user