fix(app-iced): 复审 r1 的四条 p2 + 三条小事(M250)

- p2-1 删死状态:`Accent::scheme` 字段 / `scheme()` 取值口 / `set_flavor` 重派生全仓零读者,
  且注释谎称「Ui 的唯一入口」。色值 `color` 与深浅无关,`Ui` 由 `rebuild_ui` 重算,
  所以整条删掉,`new/set/reset` 也不再收 `flavor`
- p2-2 规则只定义一次:新增 `theme::tokens(flavor, accent, scheme, families) -> (Ui, Colors)`,
  `App::rebuild_ui` 与出图 harness 都调它(harness 传 scheme=None,如实说明不覆盖 D38 分支);
  README 改成「同一条规则、不是同一个调用点」
- p2-3 逐键不闪红:`Accent` 区分「显示中的错」与 `pending`。只有**不可能再变合法**(非 hex / 超 6 位)
  才立刻亮;位数不足先挂 pending,等 `commit()`(点到别的外观控件)或配置读回才亮
- p2-4 订正 `app/state.rs:151` 那句反话(强调色现在是 UI 写入字段)
- 小事:hint 文案改「中性表面与错误色不随强调色变」;`Digit{ch}` 里的不可见字符显示成 U+XXXX;
  8 颗色块补 tooltip(hex 原文,Overlay 底)

判据:新用例 `accent_errors_only_appear_when_they_are_actionable` 覆盖「输入中不亮 / 提交后亮 /
配置读回立刻亮」三态;gallery 出图逐字节与重构前相同(规则搬迁没改像素)。
This commit is contained in:
AskaEth committed 2026-11-11 15:46:10 +08:00
1 parent c609827c49
commit 14282f3325
10 files changed
+261 -78

No files matched your search

@@ -19,7 +19,8 @@
//! 颜色本身。所以色块 = `ShapeScale::Full` 的实心圆 + 描边;选中态加粗描边(不靠色相区分:
//! 色块底下垫的是它自己的颜色,色相对比不可靠)。
use iced::widget::{button, column, container, row, Space as Gap};
use iced::widget::tooltip::{self, Tooltip};
use iced::widget::{button, column, container, row, text, Space as Gap};
use iced::{Background, Border, Element, Length, Padding, Shadow, Theme as IcedTheme};
use unlucky_design::color::Role;
@@ -129,7 +130,7 @@ fn swatch<'a>(ui: &'a Ui, hex: &'static str, selected: bool) -> Element<'a, Mess
let width = if selected { STROKE_PX * 2.0 } else { STROKE_PX };
let radius = ui.radius(ShapeScale::Full, diameter);
button(
let dot: Element<'a, Message> = button(
Gap::new()
.width(Length::Fixed(diameter))
.height(Length::Fixed(diameter)),
@@ -150,7 +151,32 @@ fn swatch<'a>(ui: &'a Ui, hex: &'static str, selected: bool) -> Element<'a, Mess
snap: true,
})
.on_press(Message::Appearance(Change::Accent(hex.to_owned())))
.into()
.into();
// 色块本体只有颜色、没有字:补一条可读提示(hex),否则键盘与读屏用户只能靠位置辨色。
Tooltip::new(dot, hint(ui, hex), tooltip::Position::Top).into()
}
/// 色块的悬停提示:`Overlay` 底 + `BodySmall` 的 hex 原文(与 `pages/agent` 的 tooltip 同一手法)。
fn hint<'a>(ui: &'a Ui, hex: &'a str) -> iced::widget::Container<'a, Message> {
let style = ui.style(unlucky_design::typography::TypeRole::BodySmall);
container(
text(hex.to_owned())
.size(style.size_px)
.font(ui.font_of(style))
.color(ui.color(Role::OnSurface)),
)
.padding(Padding::from([ui.space(Space::X1), ui.space(Space::X2)]))
.style(move |_theme: &IcedTheme| container::Style {
background: Some(Background::Color(
ui.layer(unlucky_design::elevation::MaterialLayer::Overlay),
)),
border: Border {
radius: ui.radius_dp(ShapeScale::Small),
..Border::default()
},
..container::Style::default()
})
}
/// 结构化原因 → 一句话(参数由 Rust 侧格式化,词表只认 `{name}`)。
@@ -169,7 +195,20 @@ fn reason(lang: Lang, error: AccentError) -> String {
AccentError::Digit { ch } => tr(
lang,
Key::SettingsAppearanceAccentDigit,
&[("ch", &ch.to_string())],
&[("ch", &visible(ch))],
),
}
}
/// 把一个字符变成**看得见**的形态:控制字符 / 空白 / 零宽与双向控制符(`Cf`)一律写 `U+XXXX`。
///
/// 为什么不能直接 `ch.to_string()`:用户粘进来的可能是不换行空格、零宽空格或 RTL 覆盖符——
/// 那几个字符在句子里**看不见**,错行会变成「强调色里有非十六进制字符:」后面一片空白,
/// 用户根本不知道自己错在哪。
fn visible(ch: char) -> String {
if ch.is_alphanumeric() || ch.is_ascii_graphic() {
ch.to_string()
} else {
format!("U+{:04X}", u32::from(ch))
}
}