fix(app-iced): 禁用态与遮罩改从品牌板合成(M229 复审 F1)+ 选择类图注回填(F2)
F1(核心):三处 `Scheme::baseline(ui.flavor())` 自己重算了一份方案,绕开 `Ui` 里注入的品牌板,
于是 Ink 板上的禁用控件与对话框遮罩仍按 tone 派生的天蓝中性合成(复审量到滑块轨道 #58646C、
填充框 #222E36、遮罩 #060E13,全是蓝系)。修法:`Ui::scheme()` 公开已注入的那份,
`interaction/disabled.rs`(两族合成 + 内容色)、`feedback/dialog.rs`(scrim)、
`feedback/action.rs`(禁用动作文字)改为从它取。
修完 Ink 下实测:禁用容器(12%)#292F28、按钮容器(10%)#252B24、遮罩 #0A0E0A(绿系中性,
R = B);Silver 档遮罩 #A9A9AA。旧三个蓝系值在 7 张深色图里出现次数均为 0。
回归门:`disabled_colors_follow_the_brand_board_not_the_tone_baseline`(按板合成 + 与 baseline
不等 + 深色档 G ≥ B)、`scrim_is_scrimalpha_precomposited_onto_the_canvas` 增加「不许退回 baseline」。
F2:`selection/{matrix,mod,checkbox,radio,geometry}.rs` 里「20×20 / rx2 / 外圈 20」回填成实际渲染的
「18×18 / rx4 / 外圈 18」(勾形比例不变 ⇒ 10.8dp),并按塔裁决把搜索药丸容器从
`SurfaceContainerHigh` 抬到 **`SurfaceContainerHighest`**(贴规格卡,与 filled 字段同底);
`search.rs` / `probe/search.rs` / gallery `inputs` 图注同步。展开的搜索面板与菜单是浮层,
仍留 High 档(已在文档里写明不是漏改)。
`interaction/disabled.rs` 加测试后 405 行 ⇒ 单测纯搬移到 `interaction/disabled/tests.rs`。
This commit is contained in:
1 parent
f9877a89d8
commit
4ce24d6fbd
18 files changed
+361
-213
No files matched your search
@@ -47,8 +47,9 @@ use crate::theme::{to_iced, Ui};
|
||||
/// 之前这里写死 `OnSurfaceVariant` 且只有一个「底」,导致 FilledTonal 与 IconButton 两族偏浅
|
||||
/// 15/255(视觉 r3 用像素量的)。
|
||||
pub fn disabled_content_over_role(ui: &Ui, role: Role, on_container: bool) -> Color {
|
||||
let scheme = Scheme::baseline(ui.flavor());
|
||||
disabled_content_with_alpha(&scheme, role, on_container, DISABLED_SURFACE_ALPHA)
|
||||
// **取 `Ui` 里已注入的方案**,不重算 baseline:M229 之后产品方案是品牌色板
|
||||
// (Ink / Silver),照 baseline 重算会画出旧的天蓝中性色(复审 F1)。
|
||||
disabled_content_with_alpha(ui.scheme(), role, on_container, DISABLED_SURFACE_ALPHA)
|
||||
}
|
||||
|
||||
/// 禁用态内容色——**按钮家族**(`on_container`:是否叠在本族 10% 容器上)。
|
||||
@@ -56,8 +57,7 @@ pub fn disabled_content_over_role(ui: &Ui, role: Role, on_container: bool) -> Co
|
||||
/// 与 [`disabled_content_over_role`] 的差别只有「底」的 α:按钮 10%、面状控件 12%。
|
||||
/// 两个入口分开是 M86 的结论:合成一个数会让一族永远差 1/255,肉眼看不见、只有 probe 会红。
|
||||
pub fn disabled_button_content(ui: &Ui, role: Role, on_container: bool) -> Color {
|
||||
let scheme = Scheme::baseline(ui.flavor());
|
||||
disabled_content_with_alpha(&scheme, role, on_container, DISABLED_CONTAINER_ALPHA)
|
||||
disabled_content_with_alpha(ui.scheme(), role, on_container, DISABLED_CONTAINER_ALPHA)
|
||||
}
|
||||
|
||||
/// 两个族共用的内容色求值(「底」的 α 由调用方给)。
|
||||
@@ -94,8 +94,8 @@ fn disabled_content_with_alpha(
|
||||
///
|
||||
/// **按钮家族用 [`disabled_button_colors`]**(0.10,`*ButtonTokens.DisabledContainerOpacity = 0.1f`)。
|
||||
///
|
||||
/// `ui` 不暴露 [`Scheme`],这里按同一个 flavor 求一次基线方案——调的是
|
||||
/// `unlucky_design` 的**同一个**函数与同一套角色,不是第二份令牌。
|
||||
/// 合成的输入是 [`Ui::scheme`](品牌色板 / 取色方案),**不是** `Scheme::baseline(flavor)`:
|
||||
/// 后者是 tone 派生的旧口径,在 Ink 板上会让禁用容器偏蓝(复审 F1 的根因)。
|
||||
///
|
||||
/// 顺序必须「先容器后内容」:Compose 也是这么画的,先叠容器再叠 label,
|
||||
/// 所以内容叠的角色是容器实色而不是底下的 surface。
|
||||
@@ -116,7 +116,7 @@ pub fn disabled_button_colors(ui: &Ui, surface: Role) -> (Color, Color) {
|
||||
|
||||
/// 两个族共用的求值:容器 = `OnSurface` @ `container_alpha` 叠在 `surface` 上,内容 = 38% 叠在容器上。
|
||||
fn disabled_colors_with_alpha(ui: &Ui, surface: Role, container_alpha: f32) -> (Color, Color) {
|
||||
let scheme = Scheme::baseline(ui.flavor());
|
||||
let scheme = ui.scheme();
|
||||
let base = scheme.get(surface);
|
||||
let container = scheme
|
||||
.get(Role::OnSurface)
|
||||
@@ -133,173 +133,4 @@ fn disabled_colors_with_alpha(ui: &Ui, surface: Role, container_alpha: f32) -> (
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use unlucky_design::color::SchemeFlavor;
|
||||
use unlucky_design::typography::Platform;
|
||||
|
||||
use crate::theme::Families;
|
||||
|
||||
const FAMILIES: Families = Families {
|
||||
text: "Segoe UI",
|
||||
display: "Segoe UI",
|
||||
mono: "Consolas",
|
||||
};
|
||||
|
||||
fn ui(flavor: SchemeFlavor) -> Ui {
|
||||
Ui::with_platform(flavor, Platform::Windows, FAMILIES)
|
||||
}
|
||||
|
||||
/// 两族的 α 必须都钉住(只钉一条,另一族被合并时会静默错 1/255)。
|
||||
#[test]
|
||||
fn both_disabled_alpha_classes_are_pinned_to_the_tokens() {
|
||||
// 绑定成局部变量再过断言:直接比两个常量会被 clippy 判成「常量断言」。
|
||||
let button_alpha = DISABLED_CONTAINER_ALPHA;
|
||||
let surface_alpha = DISABLED_SURFACE_ALPHA;
|
||||
let content_alpha = DISABLED_CONTENT_ALPHA;
|
||||
// 按钮容器:官方 *ButtonTokens.DisabledContainerOpacity = 0.1f
|
||||
assert!(
|
||||
(button_alpha - 0.10).abs() < f32::EPSILON,
|
||||
"按钮禁用容器 α 必须是 0.10(官方 DisabledContainerOpacity);\
|
||||
0.12 是面状控件那一类,别混用"
|
||||
);
|
||||
// 面状控件:chip 描边 / slider 未激活轨 / switch 轨道 = 0.12f
|
||||
assert!(
|
||||
(surface_alpha - 0.12).abs() < f32::EPSILON,
|
||||
"面状控件禁用 α 必须是 0.12(官方 FlatDisabledOutlineOpacity / \
|
||||
DisabledInactiveTrackOpacity / DisabledTrackOpacity)"
|
||||
);
|
||||
assert!((content_alpha - 0.38).abs() < f32::EPSILON);
|
||||
assert!(
|
||||
surface_alpha > button_alpha,
|
||||
"两族必须可区分:面状控件比按钮容器更实({surface_alpha} > {button_alpha})"
|
||||
);
|
||||
}
|
||||
|
||||
/// **面状控件族**的那一对颜色 = 0.12 预混合(chip / switch / slider / TextField 描边)。
|
||||
///
|
||||
/// ⚠️ 期望值**写死 0.12 / 0.38**,不从 `DISABLED_SURFACE_ALPHA` 取:
|
||||
/// 从被测常量求期望是**自指断言**——常量一改,expected 跟着改,永远相等(复审 r1 P2-1 实测:
|
||||
/// 把 `DISABLED_SURFACE_ALPHA` 改成 0.10,旧版这条照样绿)。现在常量改坏必红。
|
||||
#[test]
|
||||
fn surface_family_disabled_colors_are_the_12_percent_class() {
|
||||
const EXPECTED_SURFACE_ALPHA: f32 = 0.12;
|
||||
const EXPECTED_CONTENT_ALPHA: f32 = 0.38;
|
||||
let actual_alpha = DISABLED_SURFACE_ALPHA;
|
||||
assert_eq!(
|
||||
actual_alpha, EXPECTED_SURFACE_ALPHA,
|
||||
"面状控件族 α 必须是官方 0.12f;改常量会让下面每一条合成色断言同时红"
|
||||
);
|
||||
|
||||
for flavor in [SchemeFlavor::Light, SchemeFlavor::Dark] {
|
||||
let ui = ui(flavor);
|
||||
let scheme = Scheme::baseline(flavor);
|
||||
let base = scheme.get(Role::Surface);
|
||||
let expected_container = scheme
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(EXPECTED_SURFACE_ALPHA)
|
||||
.over(base);
|
||||
|
||||
let (surface, surface_content) = disabled_colors(&ui, Role::Surface);
|
||||
assert_eq!(
|
||||
surface,
|
||||
to_iced(expected_container),
|
||||
"{flavor:?}:面状控件容器 = on-surface@12% 叠表面(期望值硬编码,不取自常量)"
|
||||
);
|
||||
assert_eq!(
|
||||
surface_content,
|
||||
to_iced(
|
||||
scheme
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(EXPECTED_CONTENT_ALPHA)
|
||||
.over(expected_container)
|
||||
),
|
||||
"{flavor:?}:面状控件内容仍是 38%,只是叠的底不同"
|
||||
);
|
||||
assert_ne!(surface, ui.color(Role::Surface), "12% 叠上去必须能看出来");
|
||||
assert_eq!(surface.a, 1.0, "禁用容器也是实色(CPU/GPU 一致)");
|
||||
assert_eq!(surface_content.a, 1.0, "禁用内容也是实色");
|
||||
// 内容叠在容器上 vs 叠在表面上,是两回事(probe 量的是前者)
|
||||
assert_ne!(
|
||||
surface_content,
|
||||
to_iced(
|
||||
scheme
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(EXPECTED_CONTENT_ALPHA)
|
||||
.over(base)
|
||||
),
|
||||
"{flavor:?}:内容必须叠在容器上"
|
||||
);
|
||||
// 跨族对照:面状族的合成色必须与按钮族不同(两族一旦合流,这条红)
|
||||
let (button, _) = disabled_button_colors(&ui, Role::Surface);
|
||||
assert_ne!(
|
||||
surface, button,
|
||||
"{flavor:?}:面状族 12% 与按钮族 10% 必须落在不同像素值上"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// **按钮家族**的那一对颜色 = 0.10 预混合,且与面状控件族**必须落在不同像素值上**。
|
||||
///
|
||||
/// 期望值同样写死(0.10 / 0.38),理由见上一条:自指断言抓不住常量被改坏。
|
||||
#[test]
|
||||
fn button_family_disabled_colors_are_the_10_percent_class() {
|
||||
const EXPECTED_BUTTON_ALPHA: f32 = 0.10;
|
||||
const EXPECTED_CONTENT_ALPHA: f32 = 0.38;
|
||||
let actual_alpha = DISABLED_CONTAINER_ALPHA;
|
||||
assert_eq!(
|
||||
actual_alpha, EXPECTED_BUTTON_ALPHA,
|
||||
"按钮族 α 必须是官方 0.1f(*ButtonTokens.DisabledContainerOpacity)"
|
||||
);
|
||||
|
||||
for flavor in [SchemeFlavor::Light, SchemeFlavor::Dark] {
|
||||
let ui = ui(flavor);
|
||||
let scheme = Scheme::baseline(flavor);
|
||||
let base = scheme.get(Role::Surface);
|
||||
let expected_container = scheme
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(EXPECTED_BUTTON_ALPHA)
|
||||
.over(base);
|
||||
|
||||
let (button, button_content) = disabled_button_colors(&ui, Role::Surface);
|
||||
assert_eq!(
|
||||
button,
|
||||
to_iced(expected_container),
|
||||
"{flavor:?}:按钮容器 = on-surface@10% 叠表面(官方 DisabledContainerOpacity = 0.1f)"
|
||||
);
|
||||
assert_eq!(
|
||||
button_content,
|
||||
to_iced(
|
||||
scheme
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(EXPECTED_CONTENT_ALPHA)
|
||||
.over(expected_container)
|
||||
),
|
||||
);
|
||||
|
||||
let (surface, _) = disabled_colors(&ui, Role::Surface);
|
||||
assert_ne!(
|
||||
button, surface,
|
||||
"{flavor:?}:10% 与 12% 必须落在不同像素值上(合成一个数就是这次要防的回归)"
|
||||
);
|
||||
|
||||
// 内容色同理:按钮 38% 叠 10% 底 ≠ 面状 38% 叠 12% 底
|
||||
assert_ne!(
|
||||
button_content,
|
||||
disabled_content_over_role(&ui, Role::OnSurfaceVariant, true),
|
||||
"{flavor:?}:两族的「有容器」内容色不能同源"
|
||||
);
|
||||
assert_eq!(
|
||||
disabled_button_content(&ui, Role::OnSurfaceVariant, true),
|
||||
button_content,
|
||||
"{flavor:?}:按钮内容入口必须与按钮颜色入口同源"
|
||||
);
|
||||
// 无容器时两族完全一致(都叠 surface)——差异只在「有容器」那一支
|
||||
assert_eq!(
|
||||
disabled_button_content(&ui, Role::OnSurfaceVariant, false),
|
||||
disabled_content_over_role(&ui, Role::OnSurfaceVariant, false),
|
||||
"{flavor:?}:没有容器时不该有族差异"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
mod tests;
|
||||
@@ -0,0 +1,276 @@
|
||||
//! 禁用态两族 α 与合成色的单测(M229 复审 F1 后从 `disabled.rs` 纯搬移,守 400 行硬线)。
|
||||
//!
|
||||
//! 内容一行未改;新增的 `disabled_colors_follow_the_brand_board_not_the_tone_baseline` 是 F1 的回归门,
|
||||
//! 它与既有两条「α 族」测试放在一起,读的人一眼能看到「族」与「色源」两件事。
|
||||
|
||||
use super::*;
|
||||
use unlucky_design::color::SchemeFlavor;
|
||||
use unlucky_design::typography::Platform;
|
||||
|
||||
use crate::theme::Families;
|
||||
|
||||
const FAMILIES: Families = Families {
|
||||
text: "Segoe UI",
|
||||
display: "Segoe UI",
|
||||
mono: "Consolas",
|
||||
};
|
||||
|
||||
fn ui(flavor: SchemeFlavor) -> Ui {
|
||||
Ui::with_platform(flavor, Platform::Windows, FAMILIES)
|
||||
}
|
||||
|
||||
/// 两族的 α 必须都钉住(只钉一条,另一族被合并时会静默错 1/255)。
|
||||
#[test]
|
||||
fn both_disabled_alpha_classes_are_pinned_to_the_tokens() {
|
||||
// 绑定成局部变量再过断言:直接比两个常量会被 clippy 判成「常量断言」。
|
||||
let button_alpha = DISABLED_CONTAINER_ALPHA;
|
||||
let surface_alpha = DISABLED_SURFACE_ALPHA;
|
||||
let content_alpha = DISABLED_CONTENT_ALPHA;
|
||||
// 按钮容器:官方 *ButtonTokens.DisabledContainerOpacity = 0.1f
|
||||
assert!(
|
||||
(button_alpha - 0.10).abs() < f32::EPSILON,
|
||||
"按钮禁用容器 α 必须是 0.10(官方 DisabledContainerOpacity);\
|
||||
0.12 是面状控件那一类,别混用"
|
||||
);
|
||||
// 面状控件:chip 描边 / slider 未激活轨 / switch 轨道 = 0.12f
|
||||
assert!(
|
||||
(surface_alpha - 0.12).abs() < f32::EPSILON,
|
||||
"面状控件禁用 α 必须是 0.12(官方 FlatDisabledOutlineOpacity / \
|
||||
DisabledInactiveTrackOpacity / DisabledTrackOpacity)"
|
||||
);
|
||||
assert!((content_alpha - 0.38).abs() < f32::EPSILON);
|
||||
assert!(
|
||||
surface_alpha > button_alpha,
|
||||
"两族必须可区分:面状控件比按钮容器更实({surface_alpha} > {button_alpha})"
|
||||
);
|
||||
}
|
||||
|
||||
/// **面状控件族**的那一对颜色 = 0.12 预混合(chip / switch / slider / TextField 描边)。
|
||||
///
|
||||
/// ⚠️ 期望值**写死 0.12 / 0.38**,不从 `DISABLED_SURFACE_ALPHA` 取:
|
||||
/// 从被测常量求期望是**自指断言**——常量一改,expected 跟着改,永远相等(复审 r1 P2-1 实测:
|
||||
/// 把 `DISABLED_SURFACE_ALPHA` 改成 0.10,旧版这条照样绿)。现在常量改坏必红。
|
||||
#[test]
|
||||
fn surface_family_disabled_colors_are_the_12_percent_class() {
|
||||
const EXPECTED_SURFACE_ALPHA: f32 = 0.12;
|
||||
const EXPECTED_CONTENT_ALPHA: f32 = 0.38;
|
||||
let actual_alpha = DISABLED_SURFACE_ALPHA;
|
||||
assert_eq!(
|
||||
actual_alpha, EXPECTED_SURFACE_ALPHA,
|
||||
"面状控件族 α 必须是官方 0.12f;改常量会让下面每一条合成色断言同时红"
|
||||
);
|
||||
|
||||
for flavor in [SchemeFlavor::Light, SchemeFlavor::Dark] {
|
||||
let ui = ui(flavor);
|
||||
// 期望值按**品牌色板**(= `Ui` 注入的那一份),不是 tone 派生的 baseline。
|
||||
let scheme = unlucky_design::color::BrandBoard::for_flavor(flavor).scheme();
|
||||
let base = scheme.get(Role::Surface);
|
||||
let expected_container = scheme
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(EXPECTED_SURFACE_ALPHA)
|
||||
.over(base);
|
||||
|
||||
let (surface, surface_content) = disabled_colors(&ui, Role::Surface);
|
||||
assert_eq!(
|
||||
surface,
|
||||
to_iced(expected_container),
|
||||
"{flavor:?}:面状控件容器 = on-surface@12% 叠表面(期望值硬编码,不取自常量)"
|
||||
);
|
||||
assert_eq!(
|
||||
surface_content,
|
||||
to_iced(
|
||||
scheme
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(EXPECTED_CONTENT_ALPHA)
|
||||
.over(expected_container)
|
||||
),
|
||||
"{flavor:?}:面状控件内容仍是 38%,只是叠的底不同"
|
||||
);
|
||||
assert_ne!(surface, ui.color(Role::Surface), "12% 叠上去必须能看出来");
|
||||
assert_eq!(surface.a, 1.0, "禁用容器也是实色(CPU/GPU 一致)");
|
||||
assert_eq!(surface_content.a, 1.0, "禁用内容也是实色");
|
||||
// 内容叠在容器上 vs 叠在表面上,是两回事(probe 量的是前者)
|
||||
assert_ne!(
|
||||
surface_content,
|
||||
to_iced(
|
||||
scheme
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(EXPECTED_CONTENT_ALPHA)
|
||||
.over(base)
|
||||
),
|
||||
"{flavor:?}:内容必须叠在容器上"
|
||||
);
|
||||
// 跨族对照:面状族的合成色必须与按钮族不同(两族一旦合流,这条红)
|
||||
let (button, _) = disabled_button_colors(&ui, Role::Surface);
|
||||
assert_ne!(
|
||||
surface, button,
|
||||
"{flavor:?}:面状族 12% 与按钮族 10% 必须落在不同像素值上"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// **按钮家族**的那一对颜色 = 0.10 预混合,且与面状控件族**必须落在不同像素值上**。
|
||||
///
|
||||
/// 期望值同样写死(0.10 / 0.38),理由见上一条:自指断言抓不住常量被改坏。
|
||||
#[test]
|
||||
fn button_family_disabled_colors_are_the_10_percent_class() {
|
||||
const EXPECTED_BUTTON_ALPHA: f32 = 0.10;
|
||||
const EXPECTED_CONTENT_ALPHA: f32 = 0.38;
|
||||
let actual_alpha = DISABLED_CONTAINER_ALPHA;
|
||||
assert_eq!(
|
||||
actual_alpha, EXPECTED_BUTTON_ALPHA,
|
||||
"按钮族 α 必须是官方 0.1f(*ButtonTokens.DisabledContainerOpacity)"
|
||||
);
|
||||
|
||||
for flavor in [SchemeFlavor::Light, SchemeFlavor::Dark] {
|
||||
let ui = ui(flavor);
|
||||
// 期望值按**品牌色板**(= `Ui` 注入的那一份),不是 tone 派生的 baseline。
|
||||
let scheme = unlucky_design::color::BrandBoard::for_flavor(flavor).scheme();
|
||||
let base = scheme.get(Role::Surface);
|
||||
let expected_container = scheme
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(EXPECTED_BUTTON_ALPHA)
|
||||
.over(base);
|
||||
|
||||
let (button, button_content) = disabled_button_colors(&ui, Role::Surface);
|
||||
assert_eq!(
|
||||
button,
|
||||
to_iced(expected_container),
|
||||
"{flavor:?}:按钮容器 = on-surface@10% 叠表面(官方 DisabledContainerOpacity = 0.1f)"
|
||||
);
|
||||
assert_eq!(
|
||||
button_content,
|
||||
to_iced(
|
||||
scheme
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(EXPECTED_CONTENT_ALPHA)
|
||||
.over(expected_container)
|
||||
),
|
||||
);
|
||||
|
||||
let (surface, _) = disabled_colors(&ui, Role::Surface);
|
||||
assert_ne!(
|
||||
button, surface,
|
||||
"{flavor:?}:10% 与 12% 必须落在不同像素值上(合成一个数就是这次要防的回归)"
|
||||
);
|
||||
|
||||
// 内容色同理:按钮 38% 叠 10% 底 ≠ 面状 38% 叠 12% 底
|
||||
assert_ne!(
|
||||
button_content,
|
||||
disabled_content_over_role(&ui, Role::OnSurfaceVariant, true),
|
||||
"{flavor:?}:两族的「有容器」内容色不能同源"
|
||||
);
|
||||
assert_eq!(
|
||||
disabled_button_content(&ui, Role::OnSurfaceVariant, true),
|
||||
button_content,
|
||||
"{flavor:?}:按钮内容入口必须与按钮颜色入口同源"
|
||||
);
|
||||
// 无容器时两族完全一致(都叠 surface)——差异只在「有容器」那一支
|
||||
assert_eq!(
|
||||
disabled_button_content(&ui, Role::OnSurfaceVariant, false),
|
||||
disabled_content_over_role(&ui, Role::OnSurfaceVariant, false),
|
||||
"{flavor:?}:没有容器时不该有族差异"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// **M229 复审 F1 的回归门**:禁用态的合成必须跟着**品牌色板**走,不许偷偷回到 tone 派生的 baseline。
|
||||
///
|
||||
/// Ink 板的中性色是绿系(`on-surface` 与 `surface` 的 G 通道最高),baseline 是蓝系;所以只要
|
||||
/// 「禁用容器」在 Ink 下是绿系(`g >= b`)且与 baseline 算出来的值**不相等**,就说明它走的是板。
|
||||
///
|
||||
/// 抓得住:有人把三处 `Scheme::baseline(ui.flavor())` 写回去(那时本测试立刻红,且报出偏蓝的像素)。
|
||||
/// 抓不住:色板本身改了色相(那是 `design` 的 `anchors_pin_the_spec` 的事)。
|
||||
#[test]
|
||||
fn disabled_colors_follow_the_brand_board_not_the_tone_baseline() {
|
||||
use unlucky_design::color::BrandBoard;
|
||||
|
||||
for flavor in [SchemeFlavor::Light, SchemeFlavor::Dark] {
|
||||
let ui = ui(flavor);
|
||||
let board = BrandBoard::for_flavor(flavor).scheme();
|
||||
let baseline = Scheme::baseline(flavor);
|
||||
|
||||
// ① 面状控件族的容器(chip 描边 / slider 未激活轨 / switch 轨道)
|
||||
let (surface, _surface_content) = disabled_colors(&ui, Role::Surface);
|
||||
let expect_surface = to_iced(
|
||||
board
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(DISABLED_SURFACE_ALPHA)
|
||||
.over(board.get(Role::Surface)),
|
||||
);
|
||||
assert_eq!(
|
||||
surface, expect_surface,
|
||||
"{flavor:?}:禁用容器必须按色板合成"
|
||||
);
|
||||
|
||||
let baseline_surface = to_iced(
|
||||
baseline
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(DISABLED_SURFACE_ALPHA)
|
||||
.over(baseline.get(Role::Surface)),
|
||||
);
|
||||
assert_ne!(
|
||||
surface, baseline_surface,
|
||||
"{flavor:?}:禁用容器退回了 baseline(这就是复审 F1 的蓝系像素)"
|
||||
);
|
||||
|
||||
// ② 按钮族的容器
|
||||
let (button, _) = disabled_button_colors(&ui, Role::Surface);
|
||||
assert_ne!(button, surface, "{flavor:?}:两族 α 仍要可区分");
|
||||
|
||||
// ③ 内容色(两个入口)
|
||||
assert_eq!(
|
||||
disabled_content_over_role(&ui, Role::OnSurfaceVariant, true),
|
||||
to_iced(
|
||||
board
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(DISABLED_CONTENT_ALPHA)
|
||||
.over(
|
||||
board
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(DISABLED_SURFACE_ALPHA)
|
||||
.over(board.get(Role::Surface))
|
||||
)
|
||||
),
|
||||
"{flavor:?}:面状控件族内容色要按色板合成"
|
||||
);
|
||||
assert_eq!(
|
||||
disabled_button_content(&ui, Role::OnSurfaceVariant, true),
|
||||
to_iced(
|
||||
board
|
||||
.get(Role::OnSurfaceVariant)
|
||||
.with_alpha(DISABLED_CONTENT_ALPHA)
|
||||
.over(
|
||||
board
|
||||
.get(Role::OnSurface)
|
||||
.with_alpha(DISABLED_CONTAINER_ALPHA)
|
||||
.over(board.get(Role::Surface))
|
||||
)
|
||||
),
|
||||
"{flavor:?}:按钮族内容色要按色板合成"
|
||||
);
|
||||
|
||||
// ④ **色相判据**(像素级的自证):Ink 板的禁用容器是绿系(G 最高),baseline 是蓝系(B 最高)。
|
||||
// Ink 板:`on-surface` 与 `surface` 的 G 通道都高于 B 通道(两个中性角色都是绿黑),
|
||||
// 合成后仍是 G ≥ B;baseline 深色则是 B > G(天蓝中性)。浅色板上两者都是近中性,
|
||||
// 所以只在深色档下判色相。
|
||||
if matches!(flavor, SchemeFlavor::Dark) {
|
||||
let (r, g, b) = channels(surface);
|
||||
assert!(
|
||||
g >= b,
|
||||
"{flavor:?}:禁用容器 {r}/{g}/{b} 仍是蓝系(G < B),说明没走 Ink 板"
|
||||
);
|
||||
let (_, bg, bb) = channels(baseline_surface);
|
||||
assert!(bb > bg, "对照组不合格:baseline 深色的禁用容器应当是蓝系");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// iced 颜色 → 8bit 通道(像素值,报告里的证据就用它)。
|
||||
fn channels(color: Color) -> (u8, u8, u8) {
|
||||
(
|
||||
(color.r * 255.0).round() as u8,
|
||||
(color.g * 255.0).round() as u8,
|
||||
(color.b * 255.0).round() as u8,
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user