M172 审计:`pages/appearance/**` 中文界面下真正本地化 0/27,两个根因各断一刀。
① 拆文件(纯搬移):`section.rs` 395 行 → `section/` 五个文件,行数守卫只剩 5 行余量,
不拆就加不了任何东西。
- `section/mod.rs`(壳:section() + card() + 五个行控件 + 取词口 t)
- `section/window.rs`(① 整窗 α + ② 圆角,含 notice/说明段)
- `section/panel.rs`(③ 面板 α)、`section/bg_image.rs`(④⑤⑥⑦⑧)、
`section/colour_source.rs`(⑨)
取证:每段与原文件做「去空白/去尾逗号」diff,4/4 相同;只有五处机械改写
(`rows = rows.push(…)`→`rows.push(…)`、`t(Key::X)`→`t(Key::X, lang)`、
`super::notice`→`super::super::notice`、一处 `.into()`、rustfmt 换行)。
② 语言接到渲染层:`settings::Body` 从 `fn(&Ui, States)` 变成 `fn(&Ui, States, Lang)`,
`form` 传 `language.effective`,`settings/appearance.rs` 收 `lang` 转给
`pages::appearance::section(ui, state, lang)`,`section::t(key, lang)` 取词。
选「接进唯一那个槽位」而不是再给 `form` 加一条按下标分发的 `else if`:`settings/mod.rs`
贴着 400 行硬顶(M195 还要在此落地),这条路净增 0 行且不可能与分发表漂开。
验收点:`pages/appearance/**` 里已经搜不到 `Lang::En`。
③ 能变红的守卫:`the_appearance_group_speaks_chinese_when_the_interface_does`
—— 走 `settings::view` 真分发渲染一帧,断言取词记录里含汉字、英文那帧不含汉字且同一 key
中英译文不同。取证口是 `appearance::probe`(`#[cfg(test)]` thread_local):iced 0.14
没有可遍历控件树的公开接口(`Widget::children()` 返回状态 Tree;`operate` 要 Renderer),
所以取证点退到「字进树之前」的唯一取词口。变异证明:把 `t()` 改回 `Lang::En.catalog()`
⇒ 该用例红(失败信息逐条列出四条英文);还原 ⇒ 绿。
只接语言管子,不补文案:本组其余英文仍是字面量(行号见 M179 报告)。真实窗口截图、
macOS/Linux 字体差异未验。
87 lines
3.2 KiB
Rust
87 lines
3.2 KiB
Rust
//! 外观分组第 ① ② 段:**整窗**两件——整窗透明度滑块、窗口圆角(含 notice 与说明段)。
|
||
//!
|
||
//! 这两段是唯一**一个控件牵动另一个控件**的地方:圆角「跟随系统」时把整窗透明度调低会让圆角失效
|
||
//! (`DwmExtendFrameIntoClientArea` 的世界里没有半透明边框),所以只有这一对放在同一个文件里——
|
||
//! notice 的判据(`corner == System && window_opacity < 1.0`)跨这两段。
|
||
|
||
use iced::widget::space;
|
||
use iced::Element;
|
||
|
||
use unlucky_design::color::Role;
|
||
use unlucky_design::spacing::Space;
|
||
use unlucky_i18n::catalog::Key;
|
||
use unlucky_i18n::Lang;
|
||
use unlucky_presenter::prefs::WindowCorner;
|
||
|
||
use crate::app::components::inputs::{slider, Slider};
|
||
use crate::pages::Message;
|
||
use crate::theme::Ui;
|
||
|
||
use super::super::alpha::{window_alpha_from_fraction, window_fraction, WINDOW_ALPHA_FLOOR};
|
||
use super::super::state::State;
|
||
use super::super::Change;
|
||
use super::{choice, choice_row, note_line, slider_row, t};
|
||
|
||
/// 把 ① 整窗透明度与 ② 窗口圆角按视觉顺序推进 `rows`。
|
||
pub(super) fn push<'a>(
|
||
ui: &'a Ui,
|
||
state: &'a State,
|
||
lang: Lang,
|
||
rows: &mut Vec<Element<'a, Message>>,
|
||
) {
|
||
// ① 整窗透明度
|
||
let window_percent = format!("{:.0}%", state.window_opacity * 100.0);
|
||
rows.push(slider_row(
|
||
ui,
|
||
"Window transparency",
|
||
window_percent,
|
||
format!(
|
||
"Applies to the whole window. Floor {:.2}; 1.00 = fully opaque.",
|
||
WINDOW_ALPHA_FLOOR
|
||
),
|
||
state.window_opacity < 1.0,
|
||
slider(
|
||
Slider::new(ui, window_fraction(state.window_opacity)).on_change(|fraction| {
|
||
Message::Appearance(Change::WindowOpacity(window_alpha_from_fraction(fraction)))
|
||
}),
|
||
),
|
||
));
|
||
|
||
// ② 窗口圆角(M116):只有两档 + 一句按平台说明;不做 8/12/16 档(系统不暴露半径)。
|
||
let corner = state.window_corner();
|
||
rows.push(choice_row(
|
||
ui,
|
||
t(Key::SettingsAppearanceCorner, lang),
|
||
vec![
|
||
choice(
|
||
ui,
|
||
t(Key::SettingsAppearanceCornerSystem, lang),
|
||
corner == WindowCorner::System,
|
||
Change::Corner(WindowCorner::System),
|
||
),
|
||
choice(
|
||
ui,
|
||
t(Key::SettingsAppearanceCornerSquare, lang),
|
||
corner == WindowCorner::Square,
|
||
Change::Corner(WindowCorner::Square),
|
||
),
|
||
],
|
||
));
|
||
// **tonal notice**(r5 视觉终稿):M3 里表达「当前状态」的惯用形态是**容器**,不是改字色。
|
||
// 只在「跟随系统 + α<1」出现;与下面的说明段之间再留 `Space::X1`(列间距 X1 + 这里 X1 = X2)。
|
||
if corner == WindowCorner::System && state.window_opacity < 1.0 {
|
||
rows.push(super::super::notice(
|
||
ui,
|
||
t(Key::SettingsAppearanceCornerSuspended, lang),
|
||
));
|
||
let gap: Element<'a, Message> = space::vertical().height(ui.space(Space::X1)).into();
|
||
rows.push(gap);
|
||
}
|
||
let tone = ui.color(Role::OnSurfaceVariant);
|
||
rows.push(note_line(
|
||
ui,
|
||
t(Key::SettingsAppearanceCornerHint, lang),
|
||
tone,
|
||
));
|
||
}
|