mirror of
https://github.com/qingjian-team/qingjian.git
synced 2026-11-06 08:13:51 +00:00
feat: 悬浮状态条大小不再跟候选字号,另设大小滑块
候选字号调大后状态条跟着放大,比候选窗口还显眼(用户群反馈)。 状态条改按主题原样画,大小由 [status_bar] size(60%–150%,缺省 100%)单独定,设置的中英文页加滑块。 百分比滑块挪到共用控件,键位提示与状态条共用。
This commit is contained in:
@@ -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<Message>, 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<Message>, 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<Message>, page: &Page) -> Result<[NodeId; 3], RenderErro
|
||||
Ok([mode, size, opacity])
|
||||
}
|
||||
|
||||
/// 一行百分比滑块,步长 5%:`ratio` 是当前倍数(1 为 100%),拖到的值按整数百分比写进 `[general]`。
|
||||
fn percent_slider(
|
||||
ui: &mut Ui<Message>,
|
||||
page: &Page,
|
||||
(title, key): (&str, &'static str),
|
||||
ratio: f32,
|
||||
(min, max): (f32, f32),
|
||||
disabled: bool,
|
||||
) -> Result<NodeId, RenderError> {
|
||||
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<Message>, page: &Page) -> Result<Vec<NodeId>, RenderError> {
|
||||
let theme = &page.theme;
|
||||
|
||||
@@ -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<Message>, page: &Page) -> Result<Vec<NodeId>, 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)
|
||||
}
|
||||
|
||||
@@ -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<Message>,
|
||||
page: &Page,
|
||||
(title, (section, key)): (&str, (&'static str, &'static str)),
|
||||
ratio: f32,
|
||||
(min, max): (f32, f32),
|
||||
disabled: bool,
|
||||
) -> Result<NodeId, RenderError> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ pub struct StatusView {
|
||||
/// 配置里记住的内容左上角物理像素;`None` 首次按屏幕右下角摆。
|
||||
pub anchor: Option<(i32, i32)>,
|
||||
|
||||
/// 大小(百分比,100 为原大),与候选字号无关。
|
||||
pub size: u8,
|
||||
|
||||
/// 输入状态,给节点树画法的主题显示(与候选窗口同一份)。
|
||||
pub mode: Mode,
|
||||
}
|
||||
|
||||
@@ -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<RenderedStatus> {
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -243,6 +243,7 @@ impl StatusBar {
|
||||
&view.mode,
|
||||
self.dark.get(),
|
||||
self.dpi.get(),
|
||||
view.size,
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -421,6 +421,8 @@ enabled = false
|
||||
# 记住的屏幕位置(物理像素,拖动后自动写入);留空则首次出现在屏幕右下角
|
||||
# x = 0
|
||||
# y = 0
|
||||
# 大小(百分比,60–150);不跟候选字号走
|
||||
size = 100
|
||||
|
||||
[update]
|
||||
# 检查更新:每天向官网(qingjian.app)读一次版本索引,有新版在菜单与设置的「关于」页提示;请求不带任何标识,不自动下载安装
|
||||
|
||||
@@ -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<i32>,
|
||||
|
||||
/// 大小(百分比,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RenderedStatus, RenderError> {
|
||||
// 整条按候选字的比例缩放:去掉设置里的字号再整体乘倍数,图标、边距、点击边界跟文字一起变,与用哪个文字样式无关
|
||||
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;
|
||||
|
||||
@@ -410,7 +410,7 @@ impl Theme {
|
||||
self.animations
|
||||
}
|
||||
|
||||
/// 设置里的候选字号相对主题的比例:表格行高与状态条整体跟着它缩放。
|
||||
/// 设置里的候选字号相对主题的比例:表格行高与键位提示键盘跟着它缩放。
|
||||
pub(crate) fn candidate_scale(&self) -> f32 {
|
||||
self.sizes.ratio("candidate", |name| self.theme_size(name))
|
||||
}
|
||||
|
||||
@@ -171,15 +171,6 @@ fn render_all(mut renderer: Renderer) -> Vec<Shot> {
|
||||
);
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -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]
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -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]
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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`)。
|
||||
|
||||
@@ -62,7 +62,7 @@ description: 进入英文模式的方式、英文补全与拼写纠正、Space
|
||||
| `符` | | 打开[符号面板](shortcuts.md#符号面板) |
|
||||
| ⚙ | | 打开设置 |
|
||||
|
||||
按住拖动可移到屏幕任意位置,位置会被记住;外观随系统的深浅色与缩放。
|
||||
按住拖动可移到屏幕任意位置,位置会被记住;外观随系统的深浅色与缩放。大小在「状态条大小」里调(60%–150%),不跟候选字号走。
|
||||
它只在当前输入法为青简时显示:切换到微软拼音等其他输入法时收起,切回后再次出现;关闭某个应用不影响它。它与任务栏图标并存。
|
||||
前台是全屏应用(游戏、全屏看视频、演示)时自动收起,退出全屏后回来。也可以在任务栏「中 / 英」图标的右键菜单里随时显示或隐藏它。
|
||||
|
||||
|
||||
@@ -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 的悬浮状态条按候选字号整体缩放 |
|
||||
| 文字 | 候选字号、译文字号 | 单位为点,缺省为主题的字号;「恢复主题字号」回到主题的字号,换主题后跟随新主题 |
|
||||
| 动效 | 过渡动画 | 关闭后高亮直接跳到位,主题里的循环动画也停止 |
|
||||
|
||||
### 横排与拼音位置
|
||||
|
||||
Reference in New Issue
Block a user