diff --git a/apps/settings/src/view/pages/input.rs b/apps/settings/src/view/pages/input.rs index 71c6e683..63f7a319 100644 --- a/apps/settings/src/view/pages/input.rs +++ b/apps/settings/src/view/pages/input.rs @@ -2,9 +2,7 @@ use bamboo_slip::taffy::LengthPercentage; use bamboo_slip::taffy::{Dimension, FlexDirection, FlexWrap, NodeId, Rect, Size, Style}; -use bamboo_slip::{ - Checkbox, RenderError, SettingRow, Slider, Switch, Ui, Visual, group, section_title, -}; +use bamboo_slip::{Checkbox, RenderError, SettingRow, Switch, Ui, Visual, group, section_title}; use qingjian_platform::{KeyHintMode, Scheme, code_schemes}; use super::shortcuts; @@ -13,7 +11,7 @@ use crate::message::{Message, Popup}; use crate::shortcut::Shortcut; use crate::support::Feature; use crate::view::Page; -use crate::view::parts::controls::{select, toggle_info, toggle_with}; +use crate::view::parts::controls::{percent_slider, select, toggle_info, toggle_with}; use crate::view::parts::lists::{entry_row, import_row}; use crate::view::parts::menus::{CODE_OFF, scheme_options}; @@ -150,7 +148,7 @@ fn key_hint(ui: &mut Ui, page: &Page) -> Result<[NodeId; 3], RenderErro let size = percent_slider( ui, page, - ("键位提示大小", "key_hint_size"), + ("键位提示大小", ("general", "key_hint_size")), general.key_hint_size(), (60.0, 150.0), off, @@ -158,7 +156,7 @@ fn key_hint(ui: &mut Ui, page: &Page) -> Result<[NodeId; 3], RenderErro let opacity = percent_slider( ui, page, - ("键位提示透明度", "key_hint_opacity"), + ("键位提示透明度", ("general", "key_hint_opacity")), general.key_hint_opacity(), (30.0, 100.0), off, @@ -166,29 +164,6 @@ fn key_hint(ui: &mut Ui, page: &Page) -> Result<[NodeId; 3], RenderErro Ok([mode, size, opacity]) } -/// 一行百分比滑块,步长 5%:`ratio` 是当前倍数(1 为 100%),拖到的值按整数百分比写进 `[general]`。 -fn percent_slider( - ui: &mut Ui, - page: &Page, - (title, key): (&str, &'static str), - ratio: f32, - (min, max): (f32, f32), - disabled: bool, -) -> Result { - let percent = (ratio * 100.0).round(); - let text = format!("{percent:.0}%"); - let slider = Slider::new(percent, min, max) - .step(5.0) - .label(&text) - .disabled(disabled) - .build(ui, &page.theme, key, move |value| { - Message::set("general", key, value.round() as i64) - })?; - SettingRow::new(title) - .control(slider) - .build(ui, &page.theme) -} - /// 辅码:开关、显示、触发键,再一组码表(随包的、导入的、导入按钮)。 fn aux_code(ui: &mut Ui, page: &Page) -> Result, RenderError> { let theme = &page.theme; diff --git a/apps/settings/src/view/pages/language.rs b/apps/settings/src/view/pages/language.rs index 8a054f26..408106fa 100644 --- a/apps/settings/src/view/pages/language.rs +++ b/apps/settings/src/view/pages/language.rs @@ -8,7 +8,7 @@ use crate::message::Message; use crate::support::Feature; use crate::view::Page; use crate::view::parts::actions::with_notice; -use crate::view::parts::controls::{choice, toggle, toggle_info}; +use crate::view::parts::controls::{choice, percent_slider, toggle, toggle_info}; /// 全角 / 半角两档,配置里是布尔。 const WIDTHS: [bool; 2] = [true, false]; @@ -162,8 +162,16 @@ pub fn language(ui: &mut Ui, page: &Page) -> Result, Render ("status_bar", "enabled"), config.status_bar.enabled, )?; + let size = percent_slider( + ui, + page, + ("状态条大小", ("status_bar", "size")), + config.status_bar.size(), + (60.0, 150.0), + !config.status_bar.enabled, + )?; out.push(section_title(ui, theme, "状态条")?); - out.push(group(ui, theme, &[status])?); + out.push(group(ui, theme, &[status, size])?); } Ok(out) } diff --git a/apps/settings/src/view/parts/controls.rs b/apps/settings/src/view/parts/controls.rs index f74e3f05..9e586365 100644 --- a/apps/settings/src/view/parts/controls.rs +++ b/apps/settings/src/view/parts/controls.rs @@ -1,7 +1,7 @@ -//! 各页共用的几种设置行:开关、分段选择、下拉框。 +//! 各页共用的几种设置行:开关、分段选择、下拉框、百分比滑块。 use bamboo_slip::taffy::NodeId; -use bamboo_slip::{RenderError, Segmented, Select, SettingRow, Switch, Ui}; +use bamboo_slip::{RenderError, Segmented, Select, SettingRow, Slider, Switch, Ui}; use super::menus::anchor; use crate::message::{Message, Popup}; @@ -98,3 +98,26 @@ pub fn select( .disabled(disabled) .build(ui, &page.theme, anchor(popup), Message::OpenMenu(popup)) } + +/// 一行百分比滑块,步长 5%:`ratio` 是当前倍数(1 为 100%),拖到的值按整数百分比写进 `[section] key`。 +pub fn percent_slider( + ui: &mut Ui, + page: &Page, + (title, (section, key)): (&str, (&'static str, &'static str)), + ratio: f32, + (min, max): (f32, f32), + disabled: bool, +) -> Result { + let percent = (ratio * 100.0).round(); + let text = format!("{percent:.0}%"); + let slider = Slider::new(percent, min, max) + .step(5.0) + .label(&text) + .disabled(disabled) + .build(ui, &page.theme, key, move |value| { + Message::set(section, key, value.round() as i64) + })?; + SettingRow::new(title) + .control(slider) + .build(ui, &page.theme) +} diff --git a/apps/windows/server/src/dispatch/config.rs b/apps/windows/server/src/dispatch/config.rs index 09ba0ac3..2f31c8c2 100644 --- a/apps/windows/server/src/dispatch/config.rs +++ b/apps/windows/server/src/dispatch/config.rs @@ -118,6 +118,9 @@ pub struct RouterConfig { /// 状态条记住的位置(`[status_bar] x` / `y`,内容左上角物理像素)。 pub status_pos: Option<(i32, i32)>, + /// 状态条大小(`[status_bar] size`,百分比,已收到 60–150)。 + pub status_size: u8, + /// 拼音侧方案(`[general] scheme`)。 pub scheme: Scheme, @@ -208,6 +211,7 @@ impl From<&Config> for RouterConfig { predict: config.shortcut.predict, status_enabled: config.status_bar.enabled, status_pos: config.status_bar.x.zip(config.status_bar.y), + status_size: config.status_bar.size.clamp(60, 150), scheme: config.general.scheme(), traditional: config.general.traditional() != TraditionalVariant::Off, aux_code_key: config.general.aux_code_key(), diff --git a/apps/windows/server/src/dispatch/status/mod.rs b/apps/windows/server/src/dispatch/status/mod.rs index dea16398..a21bff3e 100644 --- a/apps/windows/server/src/dispatch/status/mod.rs +++ b/apps/windows/server/src/dispatch/status/mod.rs @@ -130,6 +130,7 @@ impl Router { full_width: self.full_width_for(english), appearance: self.config.appearance, anchor: self.config.status_pos, + size: self.config.status_size, mode: self.input_mode(), }); } diff --git a/apps/windows/server/src/dispatch/status/view.rs b/apps/windows/server/src/dispatch/status/view.rs index aba1d9a1..fd8997df 100644 --- a/apps/windows/server/src/dispatch/status/view.rs +++ b/apps/windows/server/src/dispatch/status/view.rs @@ -22,6 +22,9 @@ pub struct StatusView { /// 配置里记住的内容左上角物理像素;`None` 首次按屏幕右下角摆。 pub anchor: Option<(i32, i32)>, + /// 大小(百分比,100 为原大),与候选字号无关。 + pub size: u8, + /// 输入状态,给节点树画法的主题显示(与候选窗口同一份)。 pub mode: Mode, } diff --git a/apps/windows/server/src/ui/painter/mod.rs b/apps/windows/server/src/ui/painter/mod.rs index ee69d3b9..994c8231 100644 --- a/apps/windows/server/src/ui/painter/mod.rs +++ b/apps/windows/server/src/ui/painter/mod.rs @@ -139,16 +139,18 @@ impl Painter { self.renderer.forget(); } - /// 画状态条。 + /// 画状态条;`size` 是设置里的大小(百分比)。 pub(super) fn render_status( &mut self, cells: &[StatusCell], mode: &Mode, dark: bool, dpi: u32, + size: u8, ) -> Option { + let scale = scale(dpi) * f32::from(size) / 100.0; self.renderer - .render_status(cells, mode, &self.theme.with_dark(dark), scale(dpi)) + .render_status(cells, mode, &self.theme.with_dark(dark), scale) .inspect_err(|error| tracing::warn!(%error, "状态条渲染失败")) .ok() } diff --git a/apps/windows/server/src/ui/status/mod.rs b/apps/windows/server/src/ui/status/mod.rs index 93f1aaea..0af74152 100644 --- a/apps/windows/server/src/ui/status/mod.rs +++ b/apps/windows/server/src/ui/status/mod.rs @@ -243,6 +243,7 @@ impl StatusBar { &view.mode, self.dark.get(), self.dpi.get(), + view.size, ), _ => None, } diff --git a/crates/qingjian-platform/src/config/mod.rs b/crates/qingjian-platform/src/config/mod.rs index b7faa937..7b24ffc6 100644 --- a/crates/qingjian-platform/src/config/mod.rs +++ b/crates/qingjian-platform/src/config/mod.rs @@ -421,6 +421,8 @@ enabled = false # 记住的屏幕位置(物理像素,拖动后自动写入);留空则首次出现在屏幕右下角 # x = 0 # y = 0 +# 大小(百分比,60–150);不跟候选字号走 +size = 100 [update] # 检查更新:每天向官网(qingjian.app)读一次版本索引,有新版在菜单与设置的「关于」页提示;请求不带任何标识,不自动下载安装 diff --git a/crates/qingjian-platform/src/config/status_bar.rs b/crates/qingjian-platform/src/config/status_bar.rs index b7763f9c..b29a123d 100644 --- a/crates/qingjian-platform/src/config/status_bar.rs +++ b/crates/qingjian-platform/src/config/status_bar.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; /// `[status_bar]` 分节:桌面上常驻、可拖动的悬浮状态条(显示当前中 / 英,可选双拼方案)。 /// 与任务栏的中 / 英指示器(语言栏按钮)并存,各是一条。位置记在这里,重启后回到原处。 -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] pub struct StatusBarConfig { /// 是否显示悬浮状态条。缺省关。 @@ -13,4 +13,25 @@ pub struct StatusBarConfig { /// 记住的屏幕纵坐标(内容左上角物理像素)。 pub y: Option, + + /// 大小(百分比,60–150),见 [`Self::size`]。与候选字号无关。 + pub size: u8, +} + +impl Default for StatusBarConfig { + fn default() -> Self { + Self { + enabled: false, + x: None, + y: None, + size: 100, + } + } +} + +impl StatusBarConfig { + /// 缩放倍数,写出范围的收到 60%–150%。 + pub fn size(&self) -> f32 { + f32::from(self.size.clamp(60, 150)) / 100.0 + } } diff --git a/crates/qingjian-render/src/renderer/status/mod.rs b/crates/qingjian-render/src/renderer/status/mod.rs index d74bfe08..87ba42ae 100644 --- a/crates/qingjian-render/src/renderer/status/mod.rs +++ b/crates/qingjian-render/src/renderer/status/mod.rs @@ -66,7 +66,7 @@ impl Metrics<'_> { impl Renderer { /// 画状态条,返回位图与各格右边界(供点击命中)。`mode` 给节点树画法的主题显示中 / 英等状态。 - /// 设置里改了候选字号时整条按同一比例放大缩小。 + /// 不跟设置里的候选字号走(字号调大了状态条会比候选窗还显眼),大小由调用方乘进 `scale`。 /// 固定排法:每格宽 = 内容宽 + 两侧内边距,高 = 行高 + 内边距。 pub fn render_status( &mut self, @@ -75,8 +75,6 @@ impl Renderer { theme: &Theme, scale: f32, ) -> Result { - // 整条按候选字的比例缩放:去掉设置里的字号再整体乘倍数,图标、边距、点击边界跟文字一起变,与用哪个文字样式无关 - let scale = scale * theme.candidate_scale(); let theme = &theme.with_text_sizes(TextSizes::default()); self.stage.text().use_families(theme.families()); let spec = &theme.file().status; diff --git a/crates/qingjian-render/src/theme/mod.rs b/crates/qingjian-render/src/theme/mod.rs index ad6ce05c..dff2a464 100644 --- a/crates/qingjian-render/src/theme/mod.rs +++ b/crates/qingjian-render/src/theme/mod.rs @@ -410,7 +410,7 @@ impl Theme { self.animations } - /// 设置里的候选字号相对主题的比例:表格行高与状态条整体跟着它缩放。 + /// 设置里的候选字号相对主题的比例:表格行高与键位提示键盘跟着它缩放。 pub(crate) fn candidate_scale(&self) -> f32 { self.sizes.ratio("candidate", |name| self.theme_size(name)) } diff --git a/crates/qingjian-render/tests/snapshots.rs b/crates/qingjian-render/tests/snapshots.rs index 66ac0ac1..a08f4a0b 100644 --- a/crates/qingjian-render/tests/snapshots.rs +++ b/crates/qingjian-render/tests/snapshots.rs @@ -171,15 +171,6 @@ fn render_all(mut renderer: Renderer) -> Vec { ); shots.push(shot(format!("{name}-nihao-vertical-light"), rendered, None)); } - // 状态条整条跟候选字号缩放:字、齿轮、边距一起变 - let status = renderer - .render_status(&scenes::status_cells(), &Mode::default(), &large, 2.0) - .unwrap(); - shots.push(shot( - "large-text-status-light".to_owned(), - status.rendered, - Some(&status.cell_edges), - )); shots } diff --git a/crates/qingjian-render/tests/snapshots/macos/large-text-status-light.png b/crates/qingjian-render/tests/snapshots/macos/large-text-status-light.png deleted file mode 100644 index b36f51f6..00000000 Binary files a/crates/qingjian-render/tests/snapshots/macos/large-text-status-light.png and /dev/null differ diff --git a/crates/qingjian-render/tests/snapshots/macos/metrics.txt b/crates/qingjian-render/tests/snapshots/macos/metrics.txt index 188c680a..d81d933b 100644 --- a/crates/qingjian-render/tests/snapshots/macos/metrics.txt +++ b/crates/qingjian-render/tests/snapshots/macos/metrics.txt @@ -41,4 +41,3 @@ large-text-nihao-vertical-light bitmap 806x987 content 60,60 686x867 large-text-cloud-horizontal-light bitmap 845x314 content 60,60 725x194 large-candidate-nihao-vertical-light bitmap 762x1234 content 60,60 642x1114 large-annotation-nihao-vertical-light bitmap 934x830 content 60,60 814x710 -large-text-status-light bitmap 648x261 content 90,90 468x81 edges [230.4375, 374.4375, 468.0] diff --git a/crates/qingjian-render/tests/snapshots/windows/large-text-status-light.png b/crates/qingjian-render/tests/snapshots/windows/large-text-status-light.png deleted file mode 100644 index 8a874a71..00000000 Binary files a/crates/qingjian-render/tests/snapshots/windows/large-text-status-light.png and /dev/null differ diff --git a/crates/qingjian-render/tests/snapshots/windows/metrics.txt b/crates/qingjian-render/tests/snapshots/windows/metrics.txt index 48e14732..b562ba9a 100644 --- a/crates/qingjian-render/tests/snapshots/windows/metrics.txt +++ b/crates/qingjian-render/tests/snapshots/windows/metrics.txt @@ -41,4 +41,3 @@ large-text-nihao-vertical-light bitmap 795x987 content 60,60 675x867 large-text-cloud-horizontal-light bitmap 864x314 content 60,60 744x194 large-candidate-nihao-vertical-light bitmap 737x1234 content 60,60 617x1114 large-annotation-nihao-vertical-light bitmap 910x830 content 60,60 790x710 -large-text-status-light bitmap 646x261 content 90,90 466x81 edges [228.70313, 372.70313, 466.0] diff --git a/crates/qingjian-render/tests/theme/status.rs b/crates/qingjian-render/tests/theme/status.rs index c7eb4c21..349b2893 100644 --- a/crates/qingjian-render/tests/theme/status.rs +++ b/crates/qingjian-render/tests/theme/status.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; -use qingjian_render::{FontLibrary, Mode, Renderer, StatusCell, Theme}; +use qingjian_render::{FontLibrary, Mode, Renderer, StatusCell, TextSizes, Theme}; /// 左边按中英各画一个同尺寸不同色的方块(不用字:Linux CI 没有中文字体,两个字都画成同一个缺字框), /// 格子之间各夹一个 10 点宽的装饰,格子 4 点内边距。 @@ -89,3 +89,24 @@ fn status_tree_cells_and_mode() { chinese.rendered.pixmap.data() ); } + +/// 设置里调大候选字号,状态条不跟着变:和不调字号画出来一模一样。 +#[test] +fn status_ignores_candidate_font_size() { + let Ok(library) = FontLibrary::system("zh-CN") else { + eprintln!("没有系统字体,跳过"); + return; + }; + let mut renderer = Renderer::new(library); + let cells = [StatusCell::text("中", true), StatusCell::Gear]; + let plain = Theme::light(); + let large = plain.with_text_sizes(TextSizes::new(32.0, 22.0)); + let a = renderer + .render_status(&cells, &Mode::default(), &plain, 2.0) + .unwrap(); + let b = renderer + .render_status(&cells, &Mode::default(), &large, 2.0) + .unwrap(); + assert_eq!(a.cell_edges, b.cell_edges); + assert_eq!(a.rendered.pixmap.data(), b.rendered.pixmap.data()); +} diff --git a/docs/design/theme.md b/docs/design/theme.md index 549137e7..9486ef20 100644 --- a/docs/design/theme.md +++ b/docs/design/theme.md @@ -67,7 +67,7 @@ - **设置里的字号**:`[general] candidate_font_size` / `annotation_font_size`(0 用主题的,不限范围)由壳经 `Theme::with_text_sizes` 盖到主题上: `candidate` 与 `annotation` 换成设置的字号,行高同比例;`index`、`preedit` 与表格 `row_height` 按候选字的比例缩放(`theme/text_sizes.rs`)。 表格行高是下限(`minmax(row_height, auto)`),内容更高时撑开。 - 状态条不逐个样式改字号:`render_status` 去掉设置的字号,把整条的倍数乘上候选字的比例,文字、图标、边距、点击边界一起缩放。 + 状态条不跟设置的字号:`render_status` 去掉设置的字号按主题原样画,大小由壳按 `[status_bar] size` 乘进整条的倍数(文字、图标、边距、点击边界一起缩放)。 - **状态条**:`status.root` 是一棵与候选窗口同写法的节点树,`repeat` 绑定 `cells` 展开格子(Windows 现在是模式、标点、齿轮三格)。 格子里的条件 `emphasized`(当前模式、生效的全角标点)、`gear`、`first`、`last`,文字绑定 `text`;整条能用 `mode.*`(`Renderer::render_status` 收一份 `Mode`,Windows 的 `StatusView` 带着,与候选窗口同一个 `Router::input_mode`)。 点击按格:每格第一个产出的节点的右边界(根坐标),没画出来的格同前一格,最后一格延到内容右边;格子之间的装饰算前一格(`renderer/status/tree.rs`)。 diff --git a/docs/user/input/english-mode.md b/docs/user/input/english-mode.md index 28540c6e..72dfecd8 100644 --- a/docs/user/input/english-mode.md +++ b/docs/user/input/english-mode.md @@ -62,7 +62,7 @@ description: 进入英文模式的方式、英文补全与拼写纠正、Space | `符` | | 打开[符号面板](shortcuts.md#符号面板) | | ⚙ | | 打开设置 | -按住拖动可移到屏幕任意位置,位置会被记住;外观随系统的深浅色与缩放。 +按住拖动可移到屏幕任意位置,位置会被记住;外观随系统的深浅色与缩放。大小在「状态条大小」里调(60%–150%),不跟候选字号走。 它只在当前输入法为青简时显示:切换到微软拼音等其他输入法时收起,切回后再次出现;关闭某个应用不影响它。它与任务栏图标并存。 前台是全屏应用(游戏、全屏看视频、演示)时自动收起,退出全屏后回来。也可以在任务栏「中 / 英」图标的右键菜单里随时显示或隐藏它。 diff --git a/docs/user/settings/preferences.md b/docs/user/settings/preferences.md index b7894cb9..aad088a3 100644 --- a/docs/user/settings/preferences.md +++ b/docs/user/settings/preferences.md @@ -67,6 +67,7 @@ description: 设置窗口各页的内容、打开方式、搜索、输入法菜 | 标点 | 标点先上屏候选 | `nihao,` 一气打完得到「你好,」;关闭时标点进英文直输段 | | 标点 | 中文与英文、数字之间自动加空格(macOS、Windows) | 「用Go写了30行」上屏成「用 Go 写了 30 行」,见 [中西文自动空格](../input/english-mode.md#中西文自动空格) | | 状态条(Windows) | 悬浮状态条 | 桌面上的「中 / 英」状态条,见 [悬浮状态条](../input/english-mode.md#悬浮状态条(Windows)) | +| 状态条(Windows) | 状态条大小 | 60%–150%,缺省 100%;不随候选字号变 | ## 外观 @@ -80,7 +81,7 @@ description: 设置窗口各页的内容、打开方式、搜索、输入法菜 | 排布 | 拼音显示位置 | 行内与窗口、只在行内、只在窗口 | | 文字 | 渲染引擎 | 青简渲染器或系统绘制 | | 文字 | 字体 | 系统字族;只对青简渲染器生效 | -| 文字 | 候选字号、译文字号 | 单位为点,缺省为主题的字号;「恢复主题字号」回到主题的字号,换主题后跟随新主题;Windows 的悬浮状态条按候选字号整体缩放 | +| 文字 | 候选字号、译文字号 | 单位为点,缺省为主题的字号;「恢复主题字号」回到主题的字号,换主题后跟随新主题 | | 动效 | 过渡动画 | 关闭后高亮直接跳到位,主题里的循环动画也停止 | ### 横排与拼音位置