fix(app-iced): M79 r2 —— 四条 review 修复(对齐/单行/可比对象/整行分隔线)

代码 r1 P2-1 = 视觉 r1 P2-1(同一处,必修)
- `app_bar::action`:容器只给了 `align_y` → 24px 图标贴 40×40 命中区左缘(实测前导墨迹
  `38..58`,应 `44..68`;hover 的圆形状态层也露在图标一侧)。补 `align_x(Center)`;
  同时页头内容行补 `align_y(Center)`(`Container` 默认 Top,整行原本贴顶)。
- 把 Q21 升格成规则并全目录复查(结论写进 `navigation/mod.rs` 的表):
  app_bar 命中区/页头行、tabs tab 内容、drawer 药丸 = 两轴显式居中;
  list_item 文本块与 drawer 分组标题 = 左上起排(`Start` 是对的,不需要居中)。
  回归钉子:`navigation::probe` 的 appbar 断言量「左内缩 == 右内缩 ±2」。

代码 r1 P2-2(必修):四件文本都没有单行约束,容器高度却是固定值
- `app_bar`(标题/副标题)、`list_item::line`、`drawer::line`、`tabs` 标签统一
  `.wrapping(Wrapping::None)` + 外层 `clip(true)`;页头/ListiItem/drawer 的文本块改成
  `Fill + clip`,长文本切在文本区里,不折行、不顶穿、不压 trailing 动作。
- iced 没有 ellipsis → 按 `inputs/menu.rs` 的 D19 先例登记 **D-M79-5**
  (官方要单行 ellipsis,我们单行 + 超长直接切割)。
- 取证:gallery 新增 `ULL_NAV_PART=long`(长实例名整块)+ `navigation/probe/long.rs`
  量「行高仍 44/60/76 / 页头仍 56 / 标题墨迹右缘 < trailing 左缘」。取证块按
  `selection::matrix` 的先例收进 `navigation/matrix.rs`(gallery 单文件贴 400 红线)。

视觉 r1 P2-2(必修):ListItem 三列内容不同 → 「选中 trailing = Primary」没有可比对象
- 三列都挂 `trailing(settings)`,只有 selected 列走 `Primary`(其余 `OnSurface` / 38%)。

视觉 r1 P2-2(可选,已做):Secondary 分隔线逐 tab 分段 + 0.5 圆角留了 1px 亮缝
- 改成**整行一条**:`Stack` 把一条 `Fill` 的 1px `SurfaceVariant` 压在 tab 行底下
  (底座是 tab 行本身 → 线长 = tab 行总宽,横向滚动跟着走)。
- `TabsGeometry::bar_radius(bool)` 收成 `indicator_radius()`(只有 3px 的 Primary 条要圆角),
  单测同步;probe 新增「Secondary 分隔线必须 1 段」的断言。

门禁:`cargo fmt --all --check` ✓ · `cargo build -p unlucky-app-iced --lib` 零 warning ✓
(截图/探针/测试的实时输出见完成报告;本轮宿主机被多条并行线的 rustc 打满,
bin 链接排队严重,如未取到会如实标注未完成项。)
This commit is contained in:
AskaEth committed 2026-11-02 11:56:02 +08:00
1 parent 81e34d77da
commit a36f542f28
13 files changed
+471 -88

No files matched your search

@@ -1,35 +1,28 @@
//! gallery 里的导航类小节:四块状态矩阵(**断言在 `navigation::probe`,不在这里**)。
//! gallery 里的导航类小节:四块状态矩阵 + 一块长文案块(**断言在 `navigation::probe`**)。
//!
//! ## 为什么再切一层 `ULL_NAV_PART`
//! ## 块选择器 `ULL_NAV_PART`
//!
//! `ULL_GALLERY_SECTION=navigation` 只解决「别的家族」;本家族自己就有四块矩阵,
//! 竖排起来比 760 还高,一屏只拍得到第一块。所以这里加**块选择器**(契约 §9.2 的同一套规则:
//! 环境变量 + 默认向后兼容 + 未识别值回落并打 stderr):
//! `ULL_GALLERY_SECTION=navigation` 只隔离家族;本家族自己就有四块矩阵 + 长文案块,竖排比 760 高,
//! 一屏只拍得到第一块。所以按契约 §9.2 的同一套规则再切一层(环境变量 + 默认兼容 + 未知值回落并打
//! stderr):`ULL_NAV_PART=list|tabs|drawer|appbar|long|all`(默认 `all`)。
//!
//! ```text
//! ULL_GALLERY_SECTION=navigation ULL_NAV_PART=list # 只渲染 ListItem 矩阵
//! ULL_NAV_PART=tabs | drawer | appbar | all(默认 all)
//! ```
//!
//! 出图命令(worktree 根目录):
//!
//! ```text
//! ULL_GALLERY_SECTION=navigation ULL_NAV_PART=list ./target/debug/unlucky-launcher-iced.exe \
//! --software-renderer --screen=gallery --screenshot target/m79/list-light.png --screenshot-theme=light
//! ULL_GALLERY_SECTION=navigation ULL_NAV_PART=list ./target/debug/unlucky-launcher-iced.exe //! --software-renderer --screen=gallery --screenshot target/m79/list-light.png --screenshot-theme=light
//! ULL_NAV_DIR=target/m79 cargo test -p unlucky-app-iced navigation::probe
//! ```
//!
//! ## 矩阵形状
//!
//! ```text
//! ListItem 行 = 一 / 两 / 三行(44 / 60 / 76) 列 = 选中 / 未选中 / 禁用
//! Tabs 两行 Primary(短文案 / 长文案各选中一次)+ 一行 Secondary + 一行五态
//! Drawer rail 88 与 drawer 240 **同尺度并排**(D33:同一容器两个宽度),各 3 项、第 2 项选中
//! AppBar 静息 / 滚动两态(56 高;滚动换容器色 + L2)
//! ListItem 行 = 一 / 两 / 三行(44 / 60 / 76) 列 = 选中 / 未选中 / 禁用(三列槽位完全相同)
//! Tabs 三行 Primary(选中第 1/2/3 个)+ 一行 Secondary + 一行五态
//! Drawer rail 88 与 drawer 240 **同尺度并排**(D33),各 3 项、第 2 项 hover、第 3 项禁用
//! AppBar 静息 / 滚动两态(56 高;滚动只换容器色 + L2)
//! long 长文案:页头 + 三行 ListItem + drawer(单行约束 D-M79-5 的取证)
//! ```
//!
//! 「画在取景板(`SurfaceContainerLow`)上」不是装饰:`Surface` 的页头/导航与页面底色同色,
//! 不垫一层板子,截图里量不到边界(probe 就是靠这块板的 bbox 量 88 / 240 的)。
//! 取景板(`SurfaceContainerLow`)不是装饰:`Surface` 的页头/导航与页面底色同色,不垫一层板子,
//! 截图里量不到边界(probe 就是靠板量 88 / 240 的)。
use iced::widget::{column, container, row, text};
use iced::{Background, Element, Length};
@@ -43,7 +36,7 @@ use crate::theme::Ui;
use super::super::interaction::Interaction;
use super::super::navigation::{
app_bar, list_item, navigation, tabs, AppBar, AppBarAction, ListItem, NavGroup, NavItem,
self, app_bar, list_item, navigation, tabs, AppBar, AppBarAction, ListItem, NavGroup, NavItem,
NavLayout, Navigation, Tab, Tabs, TabsVariant,
};
use super::{heading, DemoMessage};
@@ -58,9 +51,11 @@ fn part() -> String {
};
let name = raw.trim().to_ascii_lowercase();
match name.as_str() {
"" | "all" | "list" | "tabs" | "drawer" | "appbar" => name,
"" | "all" | "list" | "tabs" | "drawer" | "appbar" | "long" => name,
_ => {
eprintln!("[gallery] {PART_ENV}={raw:?} 不是已知块(all|list|tabs|drawer|appbar),已回落 all");
eprintln!(
"[gallery] {PART_ENV}={raw:?} 不是已知块(all|list|tabs|drawer|appbar|long),已回落 all"
);
"all".to_owned()
}
}
@@ -128,6 +123,9 @@ pub(super) fn section<'a>(ui: &'a Ui) -> Element<'a, DemoMessage> {
if picked == "all" || picked == "appbar" {
body = body.push(app_bar_part(ui));
}
if picked == "long" {
body = body.push(navigation::matrix::long_part(ui, pressed));
}
body.into()
}
@@ -162,8 +160,11 @@ fn list_part<'a>(ui: &'a Ui) -> Element<'a, DemoMessage> {
for (index, lines) in ["one line", "two lines", "three lines"].iter().enumerate() {
let mut cells = row![].spacing(ui.space(Space::X2));
for state in states {
// 三列的**内容完全相同**(同样的槽位),只有态不同——否则「选中 vs 未选中」没法逐像素比
let mut item = ListItem::new(lines, pressed()).leading(icon("instances"));
// 三列的**内容完全相同**(同样的槽位),只有态不同——否则「选中 trailing = Primary」
// 这条结论没有可比对象(视觉 r1 P2-2 点名)
let mut item = ListItem::new(lines, pressed())
.leading(icon("instances"))
.trailing(icon("settings"));
if index >= 1 {
item = item.supporting("3 versions · 1.21.4");
}
@@ -171,7 +172,7 @@ fn list_part<'a>(ui: &'a Ui) -> Element<'a, DemoMessage> {
item = item.overline("RECENT");
}
item = match state {
"selected" => item.selected().trailing(icon("settings")),
"selected" => item.selected(),
// 禁用是修饰器:这里只清消息,槽位全部保留
"disabled" => item.disabled(),
_ => item.state(Interaction::Enabled),