fix(app-iced): 复审 p2 五条(方向取轨序 / 退场重开 / 遮罩口径 / 恒真断言 / 探针隔离)
- P2-1 方向改由**导航轨序**决定:新增 nav_rank(route)(NavItem::ALL 下标), Pump::observe 收 (screen, rank, detail),forward 比较 (rank, key) 而非只比屏键 —— 修掉 Logs↔Settings 的左右镜像;Download↔Content 同 rank,仍判同级水平(已登记偏离)。 - P2-2 退场途中开新框:observe_modal 先当场收尾(Hidden + 待关)再起 Opening; sync_motion 收尾时若队列里还有框就不把 dialog_model 置空。 - P2-3 只改文字:遮罩起手是**整块画布色的不透明矩形**(把页面内容遮住),不是透明看不见。 - P2-4 删掉恒真断言(单个 Option 的 <=1),写明「不累积」是字段形状 + mem::replace 给的; tests/timeline.rs 与 tests/modal.rs 的每条用例都补了「抓不住」。 - P2-5 新增 motion::configure_for_test(绕过 OS 探针),用例一律走它; 另留一条故意走真探针的用例证明 Reduced ⇒ NONE 的合成仍在产品入口上。 - 顺带:pump 的模态状态机拆到 pump/modal.rs(守 400 行硬线)。
This commit is contained in:
1 parent
4a77f1b421
commit
1edf645c2f
9 files changed
+446
-274
No files matched your search
@@ -7,6 +7,9 @@ use super::{settle, standard};
|
||||
use unlucky_design::motion::{DurationToken, MotionScale};
|
||||
|
||||
/// 模态表:进 200 / 出 150,且起点缩放来自 design 令牌(0.96)。
|
||||
///
|
||||
/// 抓得住:谁把某一腿改长/改短到不再"进长于出",或把起点缩放从令牌换回字面量。
|
||||
/// 抓不住:这套时长在真机上顺不顺眼(D30 的事)。
|
||||
#[test]
|
||||
fn modal_spec_is_short_and_asymmetric() {
|
||||
let spec = modal_spec();
|
||||
@@ -31,6 +34,7 @@ fn modal_spec_is_short_and_asymmetric() {
|
||||
/// 抓得住:①进场时长被改成 >300ms(帧数跑出去);②`Open` 还占着轨道(`is_animating` 为真 ⇒
|
||||
/// 对话框开着就一直在烧帧);③退场方向反了(`progress` 变成递增);④`take_modal_closed`
|
||||
/// 不返回 `true`(框永远关不掉)。
|
||||
/// 抓不住:缩放在**屏幕上**是从中心还是从某个角长出来的(命中与几何都没验,见报告)。
|
||||
#[test]
|
||||
fn a_modal_opens_stops_burning_frames_and_closes_in_reverse() {
|
||||
let mut pump = standard();
|
||||
@@ -82,6 +86,9 @@ fn a_modal_opens_stops_burning_frames_and_closes_in_reverse() {
|
||||
}
|
||||
|
||||
/// 弹窗被别处**硬关**(不是走 `begin_modal_close`)时不玩退场:直接收起、不残留「待关」。
|
||||
///
|
||||
/// 抓得住:把 `observe_modal(false)` 改成也起退场 ⇒ `is_animating()` 为真,本条红。
|
||||
/// 抓不住:那条硬关路径在真机上多久发生一次(`state/refresh.rs` 的 settle 分支,未统计)。
|
||||
#[test]
|
||||
fn a_dialog_closed_elsewhere_does_not_leave_a_pending_close() {
|
||||
let mut pump = standard();
|
||||
@@ -96,10 +103,13 @@ fn a_dialog_closed_elsewhere_does_not_leave_a_pending_close() {
|
||||
}
|
||||
|
||||
/// 运行期把关掉动效(`configure(NONE)`)要立刻落终态,而不是把动画「缩到 0 时长」。
|
||||
///
|
||||
/// 抓得住:把 `configure` 里 `tracks.clear()` 那段删掉(轨道留着 ⇒ 仍在占帧)。
|
||||
/// 抓不住:真实用户什么时候会中途改系统开关(那要改系统设置,见报告的未验清单)。
|
||||
#[test]
|
||||
fn switching_to_none_mid_flight_settles_immediately() {
|
||||
let mut pump = standard();
|
||||
pump.observe(Some(1), None);
|
||||
pump.observe(Some(1), Some(0), None);
|
||||
pump.advance(FRAME_MS);
|
||||
assert!(pump.is_animating());
|
||||
|
||||
@@ -145,11 +155,14 @@ fn reduced_motion_only_triggers_on_an_explicit_reduced() {
|
||||
// ------------------------------------------------------------------ 取证相位
|
||||
|
||||
/// `ULL_MOTION_AT` 的钉住相位:时间轴不前进,进度恒定(多帧序列图靠它可复现)。
|
||||
///
|
||||
/// 抓得住:把 `start_now`/`elapsed_ms` 的钉住分支删掉(进度会随时间漂,序列图不再可复现)。
|
||||
/// 抓不住:**出图本身**——这条只证明"相位恒定",图得另外拍(见报告的未验清单)。
|
||||
#[test]
|
||||
fn pinned_phase_freezes_the_clock() {
|
||||
let mut pump = Pump::new();
|
||||
pump.configure(MotionScale::STANDARD, Some(150.0));
|
||||
pump.observe(Some(1), None); // 300ms 档 ⇒ 钉在 150 = 一半
|
||||
pump.observe(Some(1), Some(0), None); // 300ms 档 ⇒ 钉在 150 = 一半
|
||||
|
||||
for _ in 0..10 {
|
||||
pump.advance(FRAME_MS);
|
||||
@@ -163,3 +176,73 @@ fn pinned_phase_freezes_the_clock() {
|
||||
);
|
||||
assert!(pump.is_animating(), "钉住的相位不会自然结束(取证专用)");
|
||||
}
|
||||
|
||||
/// **退场途中来了新框**:当场收尾旧退场、重新走进场(复审 P2-2)。
|
||||
///
|
||||
/// 覆盖三条后果里的 ①(不再按递减进度倒放)与 ③(退场到期那拍不会整层消失一帧);
|
||||
/// ②(闸门把新框的按钮全吞)由 `!modal_closing()` 间接证明——闸门判据就是它。
|
||||
///
|
||||
/// 抓得住:把 `observe_modal` 里「`Closing` 先当场收尾」那段删掉 ⇒ 本条红(progress 递减、
|
||||
/// 且 `modal_closing()` 仍为真)。
|
||||
/// 抓不住:真实 UI 上「框 A 缩到一半、框 B 从 0.96 长起来」那一帧的观感(D30)。
|
||||
#[test]
|
||||
fn a_new_modal_during_the_exit_restarts_the_entrance() {
|
||||
let mut pump = standard();
|
||||
assert!(pump.observe_modal(true), "框 A 进场");
|
||||
settle(&mut pump);
|
||||
assert!(!pump.begin_modal_close(), "关 A:退场开始");
|
||||
for _ in 0..3 {
|
||||
pump.advance(FRAME_MS);
|
||||
}
|
||||
let halfway = pump.modal_frame().progress;
|
||||
assert!(
|
||||
halfway > 0.0 && halfway < 1.0,
|
||||
"退场走到一半,实测 {halfway}"
|
||||
);
|
||||
|
||||
// 框 B 这时弹出来
|
||||
assert!(pump.observe_modal(true), "新框必须起跑新的进场轨道");
|
||||
assert_eq!(
|
||||
pump.modal_frame().progress,
|
||||
0.0,
|
||||
"①重新走进场:进度从 0 开始,而不是接着倒放"
|
||||
);
|
||||
assert!(!pump.modal_closing(), "②闸门必须松开(否则新框按钮全被吞)");
|
||||
assert!(
|
||||
pump.take_modal_closed(),
|
||||
"旧退场被当场收尾,交回一次「已关」"
|
||||
);
|
||||
|
||||
// ③之后每一帧都必须可见(不许闪一帧空的模态层)
|
||||
let mut frames = 0;
|
||||
while pump.is_animating() {
|
||||
assert!(pump.modal_frame().visible, "③中途不许整层消失");
|
||||
pump.advance(FRAME_MS);
|
||||
frames += 1;
|
||||
}
|
||||
assert!(frames > 0);
|
||||
assert_eq!(pump.modal_frame().progress, 1.0, "进场必须走到完全展开");
|
||||
}
|
||||
|
||||
/// **产品入口真的把系统偏好合成进来**(复审 P2-5 要求的「故意走真探针」那条)。
|
||||
///
|
||||
/// 断言的是**自洽**而不是某个具体值:`configure` 的结果必须等于
|
||||
/// `effective_scale(STANDARD, ReduceMotion::probe(), false)`。于是在「系统关掉动画」的机器上
|
||||
/// 它仍然是绿的(那时两边都是 `NONE`),而一旦有人把产品入口接回「直接用传入档位」就会红。
|
||||
///
|
||||
/// 抓得住:`motion::configure` 里那一行 `effective_scale(...)` 被去掉。
|
||||
/// 抓不住:宿主机上「用户设的那一格到底读对没有」(那要改系统设置,见报告的未验清单)。
|
||||
#[test]
|
||||
fn configure_goes_through_the_real_os_probe() {
|
||||
use crate::platform::motion::ReduceMotion;
|
||||
|
||||
reset();
|
||||
configure(MotionScale::STANDARD, None); // 产品入口:不绕过探针
|
||||
let expected = effective_scale(MotionScale::STANDARD, ReduceMotion::probe(), false);
|
||||
assert_eq!(
|
||||
scale(),
|
||||
expected,
|
||||
"产品入口必须真的把系统偏好合成进来(读不到 = 不改档)"
|
||||
);
|
||||
reset();
|
||||
}
|
||||
Reference in new issue
Block a user