fix(app-iced): 三处 Pixel 偏离按角色语义收敛(M248)

P2:Elevated 的容器改由高程令牌推导(Elevation::Level1.surface_role),解析值仍是
SurfaceContainerLow,图上逐像素不变;「比 Surface 高一档」从此只有 elevation.rs 一处定义,
组件里不再手抄角色名。

P3:Outlined 的 1dp 描边 OutlineVariant → Outline。design/color/mod.rs 的对比度纪律里
Outline 才是「非文字元素(目标 3:1)」,OutlineVariant 是纯装饰分隔线;图标按钮那一式本来
就是 Outline,文字按钮此前与它不一致(全站两套描边)。实测 Outline/Surface:Ink 5.08、
Silver 4.24(OutlineVariant 1.97 / 1.62)。

P4:进度轨道(线性 + 环形)SecondaryContainer → SurfaceContainerHighest——轨道是「还剩多少」
的背景尺,强调色只属于指示条。与 A1(LinearProgressIndicatorTokens.TrackColor)的差异在
progress.rs 模块文档与 geometry.rs 的 12.2 行登记,gallery 图注同步改成角色名。

判据:新增 buttons/tests/roles.rs 与进度轨道用例,断言的是「取到的角色」与 Ink / Silver /
Scheme::from_seed 三套方案下解析出的颜色,不钉任何 hex;三处各做一次变异自证(改回旧值即红)。

另修 feedback probe 的定位判据:轨道换成中性容器面后,「画布→轨道」的混色轴穿过中性文字,
按「计数」会把图注行数成轨道,改按「连续段」判(bands_by_run);顺带修掉 indeterminate_row
把标题行当主色带的既有问题(用改前的图 + 改前的探针复现过,与本次换色无关)。
This commit is contained in:
AskaEth committed 2026-11-11 14:32:26 +08:00
1 parent 81710ba38c
commit 8f5dac2ed6
10 files changed
+402 -34

No files matched your search

@@ -4,9 +4,16 @@
//!
//! | 形态 | 确定态 | 不定态 |
//! |---|---|---|
//! | [`linear`] | 轨道 `SecondaryContainer` + 指示条 `Primary`,厚 **4**、圆角 full | 官方 1750ms **双段**追逐([`phase_segments`]) |
//! | [`linear`] | 轨道 `SurfaceContainerHighest` + 指示条 `Primary`,厚 **4**、圆角 full | 官方 1750ms **双段**追逐([`phase_segments`]) |
//! | [`circular`] | 40 环 + 4 线宽,弧长 = 数值 × 360° | 官方 6000ms(弧长 0.1↔0.87 + 旋转 **0→1440°**:全局 1080° 线性 + 关键帧 360°,[`CircularGeometry::indeterminate`]) |
//!
//! **轨道角色为什么是容器面(M248,与 A1 的登记差异)**:A1 的
//! `LinearProgressIndicatorTokens.TrackColor` 是 `SecondaryContainer`,本项目取
//! `SurfaceContainerHighest`。口径是「轨道 = 进度还剩多少的**背景尺**,属于表面层级;
//! 强调色只属于指示条」。取角色而不是色值,所以换品牌板 / 换 D38 取色方案它跟着变
//! (判据见 `tests/progress.rs` 的 `track_is_the_highest_container_step_and_follows_the_scheme`)。
//! 12b §12.2 的旧值仍留在 `geometry.rs` 的参考值表里当对照。
//!
//! ## 为什么不定态要调用方给 `phase`
//!
//! 契约 §2:组件不持有时钟。`phase`(0..1 的一个周期)由页面按 `Subscription` 推进;
@@ -158,14 +165,36 @@ pub fn portion_shares(items: &[(bool, f32)]) -> Vec<(bool, u16)> {
shares
}
/// 进度家族的取色角色 `(轨道, 指示条)`。
///
/// **为什么要这个函数**:`Element` 是黑盒、`container::Style` 与 `svg::Style` 都是闭包,
/// 两者都不可内省(同 `buttons::icon_style_spec` 的教训:把 `stroke` 写死成 0 时纯函数测试照样绿)。
/// 取色点收成这一对角色,单测才能钉住「取的是哪个角色」而不只是「渲染没 panic」。
///
/// 轨道 = `SurfaceContainerHighest`(比画布高一档以上的容器面,与输入框底同层);指示条 = `Primary`。
/// 环形与线性共用同一对:一根进度条两套配色口径就没人说得清哪个才是「进度」。
pub(crate) const fn progress_roles() -> (Role, Role) {
(Role::SurfaceContainerHighest, Role::Primary)
}
/// `linear()` 真正塞进样式闭包的两个颜色(单测断言的就是它,口径同 `StyleSpec`)。
pub(crate) fn linear_colors(ui: &Ui) -> (iced::Color, iced::Color) {
let (track, active) = progress_roles();
(ui.color(track), ui.color(active))
}
/// 环形进度的取色(与 [`linear_colors`] 同源)。
pub(crate) fn circular_colors(ui: &Ui) -> (iced::Color, iced::Color) {
linear_colors(ui)
}
/// 线性进度条(轨道 + 指示条,厚 4、圆角 full)。
///
/// 轨道宽 `Fill`(由宿主决定),指示条按占比分到 `FillPortion` 上——**不需要知道轨道像素宽**,
/// 所以 50% 在任何宿主宽度下都精确是半条。
pub fn linear<'a, M: 'a>(ui: &'a Ui, progress: Progress) -> Element<'a, M> {
let geometry = LinearGeometry::of(ui);
let track_color = ui.color(Role::SecondaryContainer);
let active_color = ui.color(Role::Primary);
let (track_color, active_color) = linear_colors(ui);
let mut bar = row![]
.width(Length::Fill)
@@ -218,26 +247,19 @@ pub fn circular<'a, M: 'a>(ui: &'a Ui, progress: Progress) -> Element<'a, M> {
}
};
let track = glyph(
&geometry,
ring_source(&geometry),
ui.color(Role::SecondaryContainer),
);
let (track_color, active_color) = circular_colors(ui);
let track = glyph(&geometry, ring_source(&geometry), track_color);
// 0% 只画轨道(不画弧,也不画圆头小点);100% 画整环(`A` 命令的首尾重合画不出整圆)
let active = if sweep < MIN_SWEEP_DEG {
None
} else if sweep >= FULL_SWEEP_DEG {
Some(glyph(
&geometry,
ring_source(&geometry),
ui.color(Role::Primary),
))
Some(glyph(&geometry, ring_source(&geometry), active_color))
} else {
Some(glyph(
&geometry,
arc_source(&geometry, sweep, rotation),
ui.color(Role::Primary),
active_color,
))
};