feat(app-iced): 强调色接线落地(M250 ACCENTWIRE)
appearance.accent_color 此前是死线:配置键、presenter 往返、预设色板都在,但造 tokens 的 唯一入口恒走 DesignTokens::brand(board),用户填了色界面不变。 - theme.rs:Ui::with_accent + accent_scheme(唯一调用 DesignTokens::with_accent 的地方) - app/state/theme.rs:rebuild_ui 三选一(显式强调色 > D38 取色方案 > 出厂板),diag 一行说清是谁生效 - pages/appearance:新增 accent 状态(原文/解析结果/方案/结构化错误)+ 设置页输入行与预设色块; 非法值保持上一次合法值并给出 Short/Long/Digit 三档原因,不静默回退出厂色 - presenter::prefs:AccentChoice::parse 成为唯一解析点,set_accent_checked 交回结构化原因 - app/appearance.rs:persist_appearance 带上 accent_color(此前写不进去),只写上一次合法值 - pages/harness/app.rs:gallery 也能吃 ULL_APPEARANCE_ACCENT,供出图矩阵 - core keys.rs:订正「空 = 出厂 #0078D4;accent 令牌全域生效」这句半真半假的说明 - i18n:强调色 7 条文案(链尾新域,金表只追加) 判据:同一组件(进度条 Primary)在空配置与 #006A5C 下色值不同(App 级 + 令牌级两条); 非法值拿到结构化错误、生效色不回退、不写盘、tokens 仍可构造。
This commit is contained in:
1 parent
4cdb46d2e0
commit
e6b2200360
18 files changed
+787
-38
No files matched your search
@@ -39,6 +39,72 @@ pub const ACCENT_PRESETS: [&str; 8] = [
|
||||
"#0078D4", "#0F6CBD", "#107C10", "#9D5D00", "#C42B1C", "#8764B8", "#C239B3", "#00B7C3",
|
||||
];
|
||||
|
||||
/// 强调色输入的**结构化失败原因**(M250 ACCENTWIRE)。
|
||||
///
|
||||
/// 为什么不是 `bool`:接线之后「填坏的串」必须能变成一条看得见的原因,回退成 `false` 只会让
|
||||
/// 用户再体验一次「保存成功、界面没变」。空串**不在这里**:空 = 出厂品牌板(合法取值)。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AccentError {
|
||||
/// 去掉 `#` 后不足 6 位(`#12`、`#006A5`)。
|
||||
Short {
|
||||
/// 实际位数(原样报给用户,别把「6 位」写死在句子里)。
|
||||
digits: usize,
|
||||
},
|
||||
/// 去掉 `#` 后超过 6 位(`#006A5C00`、粘了整行的串)。
|
||||
Long {
|
||||
/// 实际位数。
|
||||
digits: usize,
|
||||
},
|
||||
/// 含非十六进制字符(`zzzzzz`)。
|
||||
Digit {
|
||||
/// 第一个不合法字符(比「格式不对」有用:用户能一眼看到错在哪个字符)。
|
||||
ch: char,
|
||||
},
|
||||
}
|
||||
|
||||
/// 一次强调色输入的解析结果:`appearance.accent_color` 的**唯一**解析点(M250):设置页输入框、
|
||||
/// 配置读回、出图取证开关都走它。少了 `#` 也接受(`006A5C` = `#006A5C`)——core 的 `normalize_hex`
|
||||
/// 同样接受,而 `ull config set appearance.accent_color 006A5C` 写出来的值必须能被界面吃下
|
||||
/// (否则 CLI 设的值会在设置页亮红行、颜色却是生效的)。输出一律规范化为大写 `#RRGGBB`。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AccentChoice {
|
||||
/// 空串 = 出厂品牌板(`DesignTokens::brand`)。**不是错误**。
|
||||
Factory,
|
||||
/// 合法的 `#RRGGBB`(大写、带 `#`)。
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl AccentChoice {
|
||||
/// 解析一段用户输入 / 配置值。
|
||||
///
|
||||
/// # Errors
|
||||
/// 位数不是 6 或含非十六进制字符时返回 [`AccentError`];空串是 `Ok(Factory)`。
|
||||
pub fn parse(text: &str) -> Result<Self, AccentError> {
|
||||
let trimmed = text.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Ok(Self::Factory);
|
||||
}
|
||||
let body = trimmed.strip_prefix('#').unwrap_or(trimmed);
|
||||
if let Some(ch) = body.chars().find(|ch| !ch.is_ascii_hexdigit()) {
|
||||
return Err(AccentError::Digit { ch });
|
||||
}
|
||||
match body.chars().count() {
|
||||
digits @ 0..=5 => Err(AccentError::Short { digits }),
|
||||
6 => Ok(Self::Custom(format!("#{}", body.to_ascii_uppercase()))),
|
||||
digits => Err(AccentError::Long { digits }),
|
||||
}
|
||||
}
|
||||
|
||||
/// 落盘字符串(`""` = 出厂;`#RRGGBB` = 自定义)。
|
||||
#[must_use]
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Factory => "",
|
||||
Self::Custom(hex) => hex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 磁贴尺寸(Q24-3:小 6 列 / 中 4 列 / 大 3 列)。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum TileSize {
|
||||
@@ -158,22 +224,21 @@ impl AppearancePrefs {
|
||||
self.bg_image_enabled && !self.bg_image.trim().is_empty()
|
||||
}
|
||||
|
||||
/// 规范化写入强调色:`#RRGGBB` 才接受(空串 = 恢复出厂)。
|
||||
///
|
||||
/// 返回 `false` 表示输入不合法(调用方据此亮设置页的 warn 行,不改现状)。
|
||||
/// 规范化写入强调色:`#RRGGBB` 才接受(空串 = 恢复出厂);返回 `false` = 输入不合法、
|
||||
/// 现状不变。需要**原因**时走 [`AppearancePrefs::set_accent_checked`](设置页走那条)。
|
||||
pub fn set_accent(&mut self, text: &str) -> bool {
|
||||
let trimmed = text.trim();
|
||||
if trimmed.is_empty() {
|
||||
self.accent_color.clear();
|
||||
return true;
|
||||
}
|
||||
match unlucky_core::setting::appearance::normalize_hex(trimmed) {
|
||||
Some(hex) => {
|
||||
self.accent_color = hex;
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
self.set_accent_checked(text).is_ok()
|
||||
}
|
||||
|
||||
/// 与 [`AppearancePrefs::set_accent`] 同一条路,但把结构化原因交出去。失败时**不动**
|
||||
/// `accent_color`(保持上一次合法值):非法串既不落盘、也不改生效色,只在 UI 上留一条原因。
|
||||
///
|
||||
/// # Errors
|
||||
/// 位数不是 6 或含非十六进制字符时返回 [`AccentError`](见 [`AccentChoice::parse`])。
|
||||
pub fn set_accent_checked(&mut self, text: &str) -> Result<(), AccentError> {
|
||||
let choice = AccentChoice::parse(text)?;
|
||||
self.accent_color = choice.as_str().to_owned();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 用预设色板第 `index` 个颜色(越界忽略)。
|
||||
|
||||
Reference in new issue
Block a user