Files
Unlucky-Launcher/crates/app-iced/src/components/navigation/mod.rs
T
AskaEth a36f542f28 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 链接排队严重,如未取到会如实标注未完成项。)
2026-11-02 11:56:02 +08:00

81 lines
4.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! **导航类家族**(`research/12b` §4/§6/§16 + `research/15` §21):四件组件,一个容器。
//!
//! | 组件 | 入口 | 参考章节 | 关键几何 |
//! |---|---|---|---|
//! | ListItem | [`list_item::list_item`] | 12b §4 | 行高 44 / 60 / 76(A1 56/72/88,登记偏离) |
//! | Tabs | [`tabs::tabs`] | 12b §6 | 容器 48、Primary 指示条 3、Secondary 线 1 |
//! | NavigationDrawer | [`drawer::navigation`] | 15 §21 + D33 | **同一个容器**:rail 88 ⇄ drawer 240 |
//! | AppBar small | [`app_bar::app_bar`] | 12b §16 | 页头 56、标题 TitleLarge、滚动换色 + L2 |
//!
//! ## 三条纪律(与另外四个家族同源)
//!
//! 1. **对消息类型泛型**,不 import `crate::app::Message`(契约 §2);`Ui` 一律 `&'a Ui` 传入。
//! 2. **状态层只走** [`crate::app::components::interaction`]:α 与禁用色一处求值;
//! 禁用是**修饰器**(`.disabled()` 只清消息,见 `selection/chip.rs` 复审 P2-1)。
//! 3. **精确尺寸只走** [`crate::app::components::layout`]:图标一律 `layout::fixed`(Q9),
//! 居中交给父级 `align_x`(Q8:`center_x(Fill)` 会把 56 宽药丸撑成 88)。
//!
//! ## 禁用内容角色**按变体**(不许抄默认值)
//!
//! | 组件 | 禁用内容角色 | 出处 |
//! |---|---|---|
//! | ListItem | **`OnSurface`**@38%(不是 `OnSurfaceVariant`) | `[源]` `ListTokens.ListItemDisabledLabel/LeadingIcon/TrailingIconColor = OnSurface` + `…Opacity = 0.38f` |
//! | Drawer / Rail 项 | `OnSurface`@38% | `[估]`(A1 无 disabled token,与 ListItem 同表面语境) |
//! | Tabs | `OnSurface`@38% | `[估]`(同上;A1 的 Tabs 两套 token 只有 Active/Inactive) |
//! | AppBar | — (本轮页头不带禁用动作) | — |
//!
//! ## Q21:要给居中就**两轴都显式给**(第三轮 review 的规则化)
//!
//! `Container` 的 `horizontal_alignment` 默认 `Left`、`vertical_alignment` 默认 `Top`
//! (`iced_widget-0.14.2/src/container.rs`)。只给 `align_y` 会让内容贴左缘——AppBar 的 40×40
//! 动作按钮里 24px 图标曾因此左内缩 4 / 右内缩 20(视觉 r1 实测前导墨迹 `38..58`,应 `44..68`),
//! 而且 hover 的圆形状态层会露在图标一侧。
//!
//! 本目录的复查结论(逐处):
//!
//! | 位置 | 需要 | 写法 |
//! |---|---|---|
//! | `app_bar::action` 命中区里的图标 | 两轴居中 | `align_x` + `align_y` |
//! | `app_bar` 页头内容行 | 垂直居中(水平由 `Fill` 决定) | `.align_y(Center)` |
//! | `tabs::tab_cell` 的内容 | 两轴居中 | `align_x` + `align_y` |
//! | `drawer/item` 的药丸 | 两轴居中 | `align_x` + `align_y` |
//! | `list_item` 的文本块 / `drawer` 的分组标题 | **左上起排**(`Start` 是对的,不需要居中) | 不设对齐,靠 `padding` 定起点 |
//!
//! 回归钉子:`navigation::probe` 的 appbar 断言量「左内缩 == 右内缩 ±2」——`align_x` 再漏一次就红。
//!
//! ## 单行约束(偏离 D-M79-5)
//!
//! 四件的文本一律 `Wrapping::None` + 外层 `clip(true)`:容器高度都是固定值(44/60/76、48/64、56),
//! `Text` 默认 `Wrapping::Word` 会把长实例名折成 2~3 行,顶穿页头、压住 trailing。官方要的是
//! **单行 ellipsis**,iced 没有 ellipsis(`inputs/menu.rs` 的 D19 先例)→ 我们单行 + 超长直接切。
//! 取证:`ULL_NAV_PART=long` 出图 + `navigation::probe::long` 量「行高不变 / 页头仍 56 / 标题不压动作」。
//!
//! ## 令牌缺口(G 编号,续 `research/15` §25 的 G13;G16~G20 已被其他家族占用)
//!
//! - **G22** Tabs 容器高 48(带图标 64)——`LayoutMetrics` 无此档;
//! - **G23** Tabs 指示条高 3 / `TabMinWidth` 90(后者 iced 无法表达,登记为限制);
//! - **G24** drawer 项高 56——与 `LM.nav_item_height`(44) 的差异按 A1 取 56,见 [`geometry`];
//! - **G25** ListItem 两/三行高(60 / 76)——`G4` 要求的 `row_height_1/2/3` 仍缺,本文件按
//! `LM.row_height` + 每行 16 派生(A1 delta 相同)。
//!
//! ## 视觉证据(D30)
//!
//! `gallery/navigation.rs` 把四个组件摆成状态矩阵;`ULL_GALLERY_SECTION=navigation` 只渲染本段,
//! `ULL_NAV_PART=<list|tabs|drawer|appbar|all>` 再切成单块出图(1180×760 一屏装不下四块矩阵)。
//! 渲染层断言在 [`probe`](读 `ULL_NAV_DIR` 下的真截图量像素),纯函数断言在各组件文件的 `tests`。
pub mod app_bar;
pub mod drawer;
pub mod geometry;
pub mod list_item;
pub mod matrix;
#[cfg(test)]
mod probe;
pub mod tabs;
pub use app_bar::{app_bar, AppBar, AppBarAction};
pub use drawer::{navigation, NavGroup, NavItem, Navigation};
pub use geometry::NavLayout;
pub use list_item::{list_item, ListItem};
pub use tabs::{tabs, Tab, Tabs, TabsVariant};