fix(app-iced): 列表行圆角改从组件表取(M287)

`list_item.rs` 硬写 `ui.radius_dp(ShapeScale::None)`,旁边还写着「4.x:ListItem 的形状
是 `CornerNone`(直角),没有圆角档」——而令牌表 `component_map::table` 的 `ListItem`
是 `Small(8)`。产品侧根本没读表,注释在替错误站台。

改为 `row_border(ui)` → `ui.radius_dp(Component::ListItem.spec().shape)`,数值只留在
令牌表。变异实测(只改表的那一行 `Small→Large`):行圆角 8 → 16,240×60 选中行的顶行
缩进 6 → 13;改回后 11 张图逐字节复原(`DevDocs/artifacts/listradius/`,不入库)。

同 commit 改掉那条直角注释、模块文档补 4.9 行,并加两条单测:渲染路径的
`Border.radius` = 表值折算;源码里不许再出现硬编码的 `ShapeScale::*` 档。

外观影响的边界(写进取证报告):行只在 选中/悬停/按下 时有容器色,未选中静息与禁用
两态 `fill` 为 `None`,那两态逐字节不变是**正确**的,不是没生效。
This commit is contained in:
AskaEth committed 2026-11-12 23:38:56 +08:00
1 parent a07cda34cb
commit 5174845cda
2 files changed
+80 -5

No files matched your search

@@ -11,6 +11,7 @@
//! | 4.5 | 字级 | 主 `BodyLarge 16/24`、辅助 `BodyMedium 14/20`、overline `LabelSmall 11/16` | `TypeRole` |
//! | 4.7 | 状态 | hover/focus/pressed **文本与图标色不变**,只叠状态层(8/10/10%) | [`interaction`] |
//! | 4.8 | 选中 trailing | 图标 → **`Primary`** | `Role::Primary` |
//! | 4.9 | 圆角 | 官方 `ListTokens` 是 `CornerNone`(直角);本项目**登记偏离**取 **8**(`Small`) | `Component::ListItem.spec().shape`(M287) |
//!
//! ## 禁用是**修饰器**,不是构造函数(照 `selection/chip.rs` 复审 P2-1 的结论)
//!
@@ -44,6 +45,7 @@ use iced::widget::{button, container, text};
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Pixels, Shadow};
use unlucky_design::color::Role;
use unlucky_design::component_map::Component;
use unlucky_design::icons::IconKind;
use unlucky_design::shape::ShapeScale;
@@ -193,6 +195,28 @@ fn palette(ui: &Ui, base: Base, state: Interaction, selected: bool) -> ItemPalet
}
}
/// 一行的圆角档位:**只从组件表取**(`Component::ListItem.spec().shape`)。
///
/// 官方 `ListTokens` 是 `CornerNone`(直角行),本项目取 `Small`(8)——列表行在启动器里是
/// 卡片内的**独立可点块**,不是贴边通栏行,圆角与卡片同族更协调(偏离登记见
/// `crates/design/src/component_map/table.rs` 的 `ListItem` 条目)。
///
/// 数值归令牌表,**产品侧不许在这里再写一份**:改表值 ⇒ 行圆角跟着变(M287 的判据)。
fn shape_scale() -> ShapeScale {
Component::ListItem.spec().shape
}
/// 一行的描边:只画圆角,四边宽度与色都不画(`Border::default()` 的其余字段)。
///
/// 单独成函数是为了让「圆角真的来自组件表」可被单测直接量到
/// (`tests::row_corner_comes_from_the_component_table`)。
fn row_border(ui: &Ui) -> Border {
Border {
radius: ui.radius_dp(shape_scale()),
..Border::default()
}
}
/// 一行文本(显式行高:三行块的 16+24+20 必须能精确等于 60,靠的就是它)。
fn line<'a, M: 'a>(
ui: &'a Ui,
@@ -285,17 +309,14 @@ pub fn list_item<'a, M: Clone + 'a>(ui: &'a Ui, item: ListItem<'a, M>) -> Elemen
let selected = item.selected;
let base = item.base;
let border = row_border(ui);
let style = move |_theme: &iced::Theme, status: button::Status| {
let state = forced.unwrap_or_else(|| Interaction::from_status(status));
let colors = palette(ui, base, state, selected);
button::Style {
background: colors.fill.map(Background::Color),
text_color: colors.headline,
// 4.x:ListItem 的形状是 `CornerNone`(直角),没有圆角档
border: Border {
radius: ui.radius_dp(ShapeScale::None),
..Border::default()
},
border,
shadow: Shadow::default(),
snap: true,
}
@@ -98,6 +98,60 @@ fn interaction_states_keep_the_content_colors() {
assert_eq!(resting.fill, None, "静息态不叠状态层");
}
/// 行的圆角**读组件表**(`Component::ListItem.spec().shape`),不是在这里另写一个常量。
///
/// 抓得住:渲染路径用的 `Border.radius` = 组件表里 `ListItem` 那一行的 `shape` 折算出的 dp;
/// 表值不是 `None`(直角),而 `Small` = 8。
/// 抓不住:有人把 `row_border` 换成一个**数值恰好相同**的字面常量(`ShapeScale::Small`)——
/// 见 `render_path_reads_the_table` 的源码结构断言,以及 M287 的变异图(改表值 ⇒ 图跟着变)。
#[test]
fn row_corner_comes_from_the_component_table() {
let ui = ui();
let spec = Component::ListItem.spec();
assert_eq!(spec.shape, ShapeScale::Small, "组件表的 ListItem 档位");
assert_eq!(spec.micro_radius, None, "ListItem 不走 D32 的微圆角层");
assert_eq!(
row_border(&ui),
Border {
radius: ui.radius_dp(spec.shape),
..Border::default()
},
"行的圆角必须就是组件表那一档折算出来的"
);
assert_eq!(row_border(&ui).radius.top_left, 8.0, "Small = 8dp");
assert_ne!(
row_border(&ui),
Border {
radius: ui.radius_dp(ShapeScale::None),
..Border::default()
},
"M287 之前这里是 `ShapeScale::None`(直角):退回去会红"
);
}
/// 源码结构(`Element` 没有内省接口,见 `pages::layout::source` 的同一条纪律):
/// 渲染路径必须**从组件表取**圆角,文件里不许再出现硬编码的圆角档。
#[test]
fn render_path_reads_the_table() {
let source = include_str!("../list_item.rs");
assert!(
source.contains("Component::ListItem.spec().shape"),
"行的圆角必须读组件表(`Component::ListItem.spec().shape`)"
);
for hardcoded in [
"ShapeScale::None",
"ShapeScale::Small",
"ShapeScale::ExtraSmall",
"ShapeScale::Medium",
"ShapeScale::Large",
] {
assert!(
!source.contains(hardcoded),
"{hardcoded} 是硬编码圆角档:ListRow 的圆角只许从组件表取"
);
}
}
/// 三档行高与 `LM.row_height` 的关系**钉死**(有人按 A1 改回 56 时这里会红)。
#[test]
fn heights_are_pinned_to_the_row_height_token() {