Files
Unlucky-Launcher/crates/app-iced/src/components/feedback/mod.rs
T
AskaEth d4e73aacdd fix(iced/feedback): 逐环改固定窗判据 + 贴顶边距 + Dialog 下限单测(复审 r3 P2-3/P2-1/P2-2)
D45 封顶轮的最后一改,只动这三条:

- **P2-3 逐环断言(假绿)**:环本来就在固定 x 栅格(24 + 56·i、宽 40),判据从「主色连通簇数
  非空」改成**六个窗逐个测**「窗内墨迹(轨道 ∪ 激活并集)外接成 40(±2)×40(±2)、且不越窗」,
  外加「1..5 号窗必须有激活色弧」。簇数只打印不再当判据。G18-i 从「欠账」改成**已收口**,
  并删掉模块文档里「要么把 6 个环摆成固定 x 栅格」那句错话(它们本来就在栅格上)。
  环带定位也换过两版(「每行墨点 4..20」会把 40 切成 32;「首行..末行」在深色档扩到 274 行),
  最终用「第一个窗里连续有墨、高度≈40 的行组」。
  变异:涂掉 4 个环 → `第 1 个窗里没有任何环墨迹:环丢了` 红;25% 环缩成 20×20 →
  `第 1 个窗里的环应 40×40(±2),实测 20×20` 红。
- **P2-1 贴顶边距**:新增「框顶行 → 条顶行 = 36±1」与「取景框高 = 96±1」两条。
  变异(`HOST_SHORT` 96 → 72,重编重拍):`D:贴底 ⇒ 框顶到条顶应为 36(96 − 48 − 12),实测 12` 红;
  还原后绿。—— 之前只量「条底距框底」,宿高回退成 72 时确实抓不住。
  顺带修了取景框判据本身:条自身(深色)比画布更接近框色,会把条自己认成框(实测差值 1),
  现在要求「框色连续段比条本身更宽」。
- **P2-2 Dialog 下限**:补单测 `effective_width(200.0) == 280.0`(360/800 两点一并钉住),
  并把「不低于官方 280」写进 `Dialog::width` 的文档。
- 欠账登记(只写表、不改代码):G18-j(动作色用全图合计,单卡漂移可能被别卡盖住)、
  G18-k(再 4 处文档残留,不为凑数扩大 diff)、G18-l(取景框判据会把条的抗锯齿底行算成框,
  余量 0.0008,方向是误报;加了「框高 96」后触发概率下降)。
2026-11-02 15:56:48 +08:00

93 lines
6.2 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.
//! **反馈类家族**:Progress(§12)/ Snackbar(§14)/ Dialog(§7)/ EmptyState(§15)。
//!
//! ## 接口(契约 §2 + §8:官方名的小写形式,对消息类型泛型,不 import `app::Message`)
//!
//! | 组件 | 入口 | 消息要求 |
//! |---|---|---|
//! | 线性进度 | [`linear(ui, Progress::determinate(0.5))`](linear) | **无消息**(进度不可点) |
//! | 环形进度 | [`circular(ui, Progress::indeterminate(phase))`](circular) | **无消息** |
//! | 提示条 | [`snackbar(ui, Snackbar::new("…").action("撤销", msg))`](snackbar) | 有动作/关闭时才要 `M` |
//! | 对话框 | [`dialog(ui, Dialog::new("标题", "正文").double(..))`](dialog) | 有动作时要 `M` |
//! | 空态 | [`empty_state(ui, EmptyState::new(icon, "标题", "说明"))`](empty_state) | 有 CTA 时要 `M` |
//!
//! 全部 `M: Clone + 'a`(与按钮家族同一口径),`Ui` 一律 `&'a Ui` 传入、组件内不 `Ui::new`。
//!
//! ## 三条硬口径(每个组件文件开头各有一套细则表)
//!
//! 1. **数值来自哪里**:颜色/字级/圆角/内距走令牌(`Role` / `TypeRole` / `ShapeScale` / `Space`),
//! 进度厚度、Snackbar 宽高、Dialog 宽、空态块这四组令牌层没有 → 登记 **G17**([`geometry`])。
//! 2. **状态层只有一处**:hover/pressed 的叠色一律经 [`interaction::overlay_color`];
//! 禁用内容默认经 [`interaction::disabled_content_over_role`],但**动作件是个例外**——
//! `action.rs` 的 `content_color()` 把 38% 内容色叠在它**自己的容器角色**上
//! (`InverseSurface` / `SurfaceContainerHigh`),因为 `interaction` 那两档只认
//! 「叠在 Surface / 12% 容器上」;
//! 禁用是**状态**(`on_press: Option<M>` 为 `None`),不是另一个构造器(M77 之前 Chip 踩过)。
//! 3. **精确尺寸经 `layout`**:图标/几何图元一律 `layout::fixed` / `layout::centered_fixed`(Q9),
//! 居中一律父级 `align_x/align_y`(Q8,不许 `center_*`)。
//!
//! ## CPU 降级(D27)与「不画阴影」
//!
//! 本家族零模糊、零运行时半透明:进度条是实色、Dialog 的 scrim 是**预合成实色**
//! ([`scrim`]),两个浮层都不画阴影(`ShadowSpec` 还是 M63 占位值 + D27 禁模糊)。
//! 四条偏离逐条登记在报告里(D-1 无阴影 / D-2 scrim 预合成 / D-3 波浪未实现 / D-4 Snackbar 无阴影),
//! 另有一条**观察项** D-5(深色档 scrim 只压暗 4%,见 `dialog.rs` 的 scrim 一节)。
//!
//! ## 逐项偏离登记(提交报告里同一份表)
//!
//! | 编号 | 参考 | 我们 | 理由 | 可修 |
//! |---|---|---|---|---|
//! | D-1 | Dialog L3 阴影 | 无阴影 | D27 CPU 禁模糊 + `ShadowSpec` 为 M63 占位 | 是(等 M63 阴影表 + 双档渲染) |
//! | D-2 | scrim 真半透明 @0.32 | 预合成实色 | D27 CPU 零半透明;遮罩下是底色时逐像素等价 | 是(GPU 档可切真 α) |
//! | D-3 | 线性波浪 3/40、环形 1.6/15 | 直线段 / 等弧长 | `12b §12.4` 明确允许,登记后用匀速替代 | 是 |
//! | D-4 | Snackbar L3 阴影 | 无阴影 | 同 D-1 | 是 |
//! | G17-d | Snackbar 最小宽 344 | 组件不强制(宿主保证) | iced `Length` 无 min-width(同按钮 G15) | 是(自定义 Widget) |
//! | D-5 | 深色档 scrim 压暗 32% | 保持 0.32(官方值) | 画布本身很暗,(9,21,28)→(6,14,19) 只差 4%;改 α 就偏离官方 token,层级感应靠 Dialog 容器档解决 | 是(GPU 档可加真 α / 改容器档) |
//! | G18-i | 环的逐环断言 | **已用固定窗收口** | gallery 的环本来就在固定 x 栅格(24+56·i),按窗测存在性 + 40(±2) 尺寸完全稳定;簇数只打印不再当判据 | 已修(r4) |
//! | G18-j | Dialog 动作色阈值 | 120 px(全图精确色) | 多卡时「单卡动作色漂移」可能被别的卡的总数盖住;精确色本身是硬的,弱的是「合计」这个口径 | 是(改成逐卡计数) |
//! | G18-k | 文档残留 | 本轮只改了能确证的行 | 复审又找出 4 处同类残留;不为凑数扩大 diff,留待下次触碰该文件时顺手改 | 是 |
//! | G18-l | snackbar 取景框判据 | 「更靠近框色而不是画布」 | 条自身的抗锯齿底行覆盖率实测 0.5008(余量 0.0008),可能被算成框 ⇒ **误报方向**(不是假绿);加了「框高 96」之后触发概率下降 | 是(改用精确色 + 容差,或自绘取景框) |
//!
//! ## 文件地图
//!
//! ```text
//! geometry.rs G17 常量 + 五组几何(含不定态 keyframes)
//! keyframes.rs 相位 → 进度 的纯函数求值器(组件不计时)
//! progress.rs linear / circular(含环形 SVG 图元)
//! snackbar.rs 提示条 + 贴顶/贴底
//! dialog.rs 对话框 + scrim
//! empty_state.rs 空态(项目自定:`[估]` 几何 + `[规格]` 排版 + 一条 `[源]` CTA)
//! action.rs 文字动作 / 图标动作(Dialog 与 Snackbar 共用)
//! gallery_parts.rs 家族自己的 gallery 段落开关(`ULL_FEEDBACK_PART`)
//! probe.rs 渲染层像素断言(对着 gallery 截图量,见其模块文档)
//! ```
//!
//! ## 桩期的 `demo()` 已删除(不是签名被改)
//!
//! M75 的占位 `demo()` 只画一行「pending」文字,没有任何验收价值;真组件的验收画面就是
//! `gallery/feedback.rs` 的四段矩阵。契约 §9.3「家族 `demo()` 签名是共享契约」约束的是
//! **别改别人的调用点**——`feedback::demo` 自始至终只有 `gallery/feedback.rs` 一个调用点,
//! 而那个文件就在本 mission 的 scope 内,所以这里连同调用点一起换成了真组件(不留死代码:
//! 一个没人调用的 `pub fn` 会直接触发 `dead_code`,`-D warnings` 门禁会红)。
mod action;
pub mod dialog;
pub mod empty_state;
pub mod gallery_parts;
pub mod geometry;
pub mod keyframes;
pub mod panel;
pub mod progress;
pub mod snackbar;
#[cfg(test)]
mod pixel;
#[cfg(test)]
mod probe;
#[cfg(test)]
mod tests;
pub use dialog::{dialog, scrimmed, Dialog};
pub use empty_state::{empty_state, EmptyState};
pub use progress::{circular, linear, Progress};
pub use snackbar::{snackbar, Placement, Snackbar};