fix(app-iced): M125 chips 档列表不被选项区压成缝;Compact 档主版本首项不再裁字
两条缺陷都是 M123 取证时在真图上拍出来的,塔台另行核过图: 缺陷 1(medium):chips 档(窗口 < 736,可用宽 < 600)里选项区按内容高 ≈316px 钉在列表下面,列表只剩 18px(0.4 行,行高 44)——版本号读不出来、 换不了版本。修法:`rows::TailShare` 两档,chips 档改成「列表与面板各吃一半、 面板自己可滚」(列表拿到 ≈182px = 4 整行,面板顶端那个实心「安装」与五个 loader chip 仍在屏内);三栏三档照旧钉死(760 高下列表还有 3 整行、面板 整块在屏内,两样都对)。判据按档位而不是按高度:页面模型里只有 window_width, 窗口高度没接进页面(边界写进 TailShare 文档与报告)。 缺陷 2(medium):Compact 档主版本栏宽 64,`ListItem` 两侧内边距各 16 ⇒ 文本格 32,首项「All versions」要 ≈84 ⇒ 图上只剩「All v」;中文「全部版本」= 4 全角字 = 64,32 里只剩「全部」且正好顶到裁切边。修法:图标条档的「全部版本」不画文字, 改画与本页第一级那个「全部」同一个网格图标(40×40 Tonal 药丸 + 全称 tooltip); 桶标签(1.21/24w,实测墨迹 26)放得下,照旧画原文。`rails.rs` 那条「64 宽里 1.21 仍完整」的注释改成实测宽度表,并写清它对首项不成立。 顺带:列表与尾部面板之间补上分隔线(三态一致),`rows::content` 与两个装配点 按档位传 `TailShare`。 守门: - `tests/options_shot.rs` 解掉 M123 那条 `#[ignore]`(期望值一个字没改,只加 「列表区 ≥ 3 整行」与「面板仍在屏内、没被底边切」两条); - 新增 `a_compact_tier_shot_draws_an_icon_for_the_first_major_entry`:缺陷 2 的 像素守门(首项是 40×40 居中药丸,不是 64×44 通栏文字行)。复审建议的 「墨迹右边界 ≤ 栏右内边界」在坏图上也是绿的(修前墨迹 216..244,边界 248), 所以改钉形状,理由写在测试文档里; - `tests/rails.rs`:中文与英文都走 Icon 分支、以及 32px 文本格的宽度账 (「全部版本」= 64 > 32 是精确值,「全部」= 32 零余量所以不选短标签); - `tests/view.rs`:档位 → TailShare 的决策面 + 两个装配点都按档位传。 未改产品文案、未改窗口最小宽、未动 M123 的任何断言期望值。
This commit is contained in:
1 parent
df0e98d811
commit
822f98022d
7 files changed
+444
-47
No files matched your search
@@ -37,8 +37,10 @@ const FAMILIES: Families = Families {
|
||||
|
||||
/// 1180 宽那张(`01-options-fabric-en-light.png`)。
|
||||
const WIDE_SHOT: &str = "ULL_DL_OPTIONS_SHOT";
|
||||
/// 700 宽那张(`03-options-fabric-en-light-chips700.png`)——只在 `--ignored` 那条里用。
|
||||
/// 700 宽那张(`02-options-fabric-en-light-chips700.png`)——chips 档回归守门用。
|
||||
const CHIPS_SHOT: &str = "ULL_DL_CHIPS_SHOT";
|
||||
/// 736 宽那张(`04-options-fabric-en-light-compact736.png`)——Compact 档主版本栏用。
|
||||
const COMPACT_SHOT: &str = "ULL_DL_COMPACT_SHOT";
|
||||
|
||||
fn ui() -> Ui {
|
||||
Ui::with_platform(SchemeFlavor::Light, Platform::Windows, FAMILIES)
|
||||
@@ -123,6 +125,44 @@ impl Shot {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// `y` 这一行上、含 `x` 的那段**连续非底色**区:`(起点, 长度)`。
|
||||
///
|
||||
/// 与 [`Shot::run_at`] 的区别:颜色可以变(药丸底色 + 里面的图标墨迹算同一块)。
|
||||
fn fill_run(
|
||||
&self,
|
||||
y: usize,
|
||||
left: usize,
|
||||
right: usize,
|
||||
x: usize,
|
||||
background: [u8; 3],
|
||||
) -> (usize, usize) {
|
||||
let filled = |x: usize| self.at(x, y) != background;
|
||||
let mut start = x;
|
||||
while start > left && filled(start - 1) {
|
||||
start -= 1;
|
||||
}
|
||||
let mut end = x;
|
||||
while end + 1 < right && filled(end + 1) {
|
||||
end += 1;
|
||||
}
|
||||
(start, end - start + 1)
|
||||
}
|
||||
|
||||
/// 列表列里、`from` 起的**第一条薄通栏横线**的 y(`divider::full` 画的那条 1px 线)。
|
||||
///
|
||||
/// 用它量「版本列表到哪儿为止」:尾部面板上沿有一条通栏分隔线,它上面的高度就是列表拿到的。
|
||||
/// 只认**薄**线(上下两行都与它不同色)——选中行的底色带也是通栏的,那是 44px 的块,不是线。
|
||||
fn first_rule_y(&self, left: usize, right: usize, from: usize, background: [u8; 3]) -> Option<usize> {
|
||||
let thin = |y: usize| {
|
||||
let color = self.at(left + 2, y);
|
||||
color != background
|
||||
&& (left + 2..right.saturating_sub(2)).all(|x| self.at(x, y) == color)
|
||||
&& (y == 0 || self.at(left + 2, y - 1) != color)
|
||||
&& (y + 1 >= self.height || self.at(left + 2, y + 1) != color)
|
||||
};
|
||||
(from..self.height).find(|y| thin(*y))
|
||||
}
|
||||
}
|
||||
|
||||
/// 三栏的 x 区间,全部从**产品自己的裁决函数**推出来(不是手抄的像素坐标)。
|
||||
@@ -258,18 +298,26 @@ fn an_options_shot_shows_both_rails_and_the_whole_panel() {
|
||||
);
|
||||
}
|
||||
|
||||
/// `02-options-fabric-en-light-chips700.png`(700×760)——**这条现在是红的**(M123 的缺陷登记)。
|
||||
/// `02-…-chips700.png`(700×760,`ULL_DL_CHIPS_SHOT`)——**M125 缺陷 1 的回归守门**。
|
||||
///
|
||||
/// 现状(2026-11-05,tiny-skia,700×760,`ULL_DL_DEMO=options-fabric`):Chips 档多出两行 chip,
|
||||
/// 选项区又整块钉在列表下面,于是列表列只剩 ~22px 的缝,选中行的底色带实测 19px(行高 44)。
|
||||
/// 断言写的是**该有的**结果;修好之后删掉 `#[ignore]` 就是它的回归守门。
|
||||
/// 修前(M123 取证,2026-11-05,tiny-skia,700×760,`ULL_DL_DEMO=options-fabric`):
|
||||
/// chips 档多出两行 chip,选项区又整块钉在列表下面(316px),于是列表列只剩一条 **18px** 的缝,
|
||||
/// 选中行的底色带实测 19px(行高 44)——版本号一个字读不出来,用户换不了版本。
|
||||
/// 修法见 `rows::TailShare`:chips 档改成「列表与面板各吃一半,面板自己可滚」。
|
||||
///
|
||||
/// ```text
|
||||
/// ULL_DL_CHIPS_SHOT=02-options-fabric-en-light-chips700.png \
|
||||
/// cargo test -p unlucky-app-iced --lib options_shot -- --ignored
|
||||
/// cargo test -p unlucky-app-iced --lib options_shot
|
||||
/// ```
|
||||
///
|
||||
/// 抓得住:①首行版本行的底色带 = 44(整行在屏);②**至少 3 整行**读得出来(数墨迹条数);
|
||||
/// ③选项面板仍在屏内(列表列最后一行墨迹明显低于版本行区,且没贴到底边)——
|
||||
/// 「能换版本」与「能看见选项」是这条断言的成对验收。
|
||||
///
|
||||
/// 抓不住:文本有没有被**横向**裁掉(那要看字形宽度,见 `tests/rails.rs` 的标签断言与
|
||||
/// 报告里的量测表)、hover/tooltip 层(静态图拍不到)、面板内部滚动到底后长什么样
|
||||
/// (静态图只有一帧,只能拍到 scroll offset 0)。
|
||||
#[test]
|
||||
#[ignore = "M123 缺陷:Chips 档(窗口 <736)列表被压成 ~19px,见报告「发现的缺陷」第 1 条"]
|
||||
fn a_chips_tier_shot_keeps_a_whole_version_row_readable() {
|
||||
let Some(path) = shot_path(CHIPS_SHOT) else {
|
||||
eprintln!("[options-shot] SKIPPED:未设置 {CHIPS_SHOT}。");
|
||||
@@ -278,12 +326,110 @@ fn a_chips_tier_shot_keeps_a_whole_version_row_readable() {
|
||||
let shot = Shot::load(&path);
|
||||
let bands = bands(700.0);
|
||||
let canvas = shot.at(bands.list.0 + 2, 12);
|
||||
let (_, row_height) = shot
|
||||
let metrics = ui().metrics();
|
||||
|
||||
// ① 首行整行 44:这是 M123 原本写的那条(修好之后解 ignore,不许改期望值迁就现状)
|
||||
let (row_top, row_height) = shot
|
||||
.first_filled_band(bands.list.0 + 60, 360, 440, canvas)
|
||||
.expect("列表列里应当有版本行");
|
||||
assert_eq!(
|
||||
row_height as f32,
|
||||
ui().metrics().row_height,
|
||||
"Chips 档的版本行被压成 {row_height}px(应为 44)"
|
||||
row_height as f32, metrics.row_height,
|
||||
"Chips 档的版本行被压成 {row_height}px(应为 {})",
|
||||
metrics.row_height
|
||||
);
|
||||
|
||||
// ② 版本列表那一块至少 3 整行高:尾部面板上沿的通栏分隔线就是列表的下界
|
||||
let right = bands.list.0 + bands.list.1 as usize - 2;
|
||||
let rule = shot
|
||||
.first_rule_y(bands.list.0, right, row_top, canvas)
|
||||
.expect("找不到尾部面板上沿的分隔线(列表区的下界量不出来)");
|
||||
let rows_px = rule.saturating_sub(row_top) as f32;
|
||||
assert!(
|
||||
rows_px >= 3.0 * metrics.row_height,
|
||||
"Chips 档的列表区只有 {rows_px}px(y={row_top}..{rule}),不足 3 整行({}):\
|
||||
尾部面板把列表压扁了",
|
||||
3.0 * metrics.row_height
|
||||
);
|
||||
|
||||
// ③ 面板在屏内:最下面一行墨迹明显低于版本行区(不是列表自己),也没贴到窗口底边
|
||||
let last = shot
|
||||
.last_ink_row(
|
||||
bands.list.0,
|
||||
bands.list.0 + bands.list.1 as usize - 2,
|
||||
canvas,
|
||||
)
|
||||
.expect("列表列里应当有内容");
|
||||
assert!(
|
||||
last > row_top + 300,
|
||||
"选项面板没画出来:最后一行墨迹在 y={last},版本行从 y={row_top} 起"
|
||||
);
|
||||
assert!(
|
||||
last + 8 < shot.height,
|
||||
"面板被窗口底边切掉:最后一行墨迹在 y={last}(图高 {})",
|
||||
shot.height
|
||||
);
|
||||
}
|
||||
|
||||
/// `04-…-compact736.png`(736×760,`ULL_DL_COMPACT_SHOT`)——**Compact 档主版本栏(缺陷 2)**。
|
||||
///
|
||||
/// 修前(M123 取证):主版本栏宽 64,`ListItem` 两侧内边距各 16 ⇒ 文本格 32,
|
||||
/// 而首项「All versions」要 ≈84 ⇒ 图上只剩「**All v**」。中文更糟:「全部版本」= 4 全角字 = 64,
|
||||
/// 32 里只剩「全部」而且正好顶到裁切边。
|
||||
///
|
||||
/// 修后:图标条档的「全部版本」**不画文字**(`rails::major_shape` 的 `Icon` 分支)——
|
||||
/// 画成与本页第一级那个「全部」同一个网格图标(40×40 `Tonal` 药丸 + 全称 tooltip)。
|
||||
///
|
||||
/// 抓得住:①首项是 **40×40 的图标药丸**(不是 64 宽 × 44 高的通栏文字行)——谁把它改回
|
||||
/// 「画文字」(全称或短标签都会是通栏 44 行)这条立刻红;②药丸在 64 栏里**居中**(左右各 12)。
|
||||
/// 抓不住:图标字形对不对、tooltip 长什么样(静态图拍不到 tooltip;字形要看图)。
|
||||
///
|
||||
/// ## 为什么不用「文字墨迹右边界 ≤ 栏右内边界」这条(复审建议的那条)
|
||||
///
|
||||
/// 它**在坏图上也是绿的**:修前那张图实测首项文字墨迹 x=216..244,而栏右内边界是
|
||||
/// `200 + 64 − 16 = 248` ⇒ 244 ≤ 248 成立——「All **v**」明明被裁掉了,断言却过。
|
||||
/// 原因是 `ListItem` 的 `clip(true)` 让墨迹**永远**停在裁切边之前,边界的比较量不出「被裁」。
|
||||
/// 所以这里改钉**形状**:文字行是 64×44 的通栏带,图标按钮是 40×40 的居中药丸,两者像素可分。
|
||||
///
|
||||
/// **中文为什么没有同档图**:本仓的截图通道 `pages::demo_model` 把 `Model::new(..)` 的
|
||||
/// `lang` 写死成 `Lang::En`(`pages/model.rs:120`),`ULL_LANG` 只作用到 app 的 `App`,
|
||||
/// 拍不出中文页面 —— 所以中文的结论走「数据面断言 + 算术」:
|
||||
/// `tests/rails.rs` 的 `only_the_first_major_entry_switches_to_an_icon_in_the_icon_rail`
|
||||
/// (中文与英文走**同一条**图标分支,键判据 `entry.key.is_none()` 与语言无关)与
|
||||
/// `the_icon_rail_text_box_is_32_and_only_bucket_labels_fit`(「全部版本」= 64 > 32)。
|
||||
#[test]
|
||||
fn a_compact_tier_shot_draws_an_icon_for_the_first_major_entry() {
|
||||
let Some(path) = shot_path(COMPACT_SHOT) else {
|
||||
eprintln!("[options-shot] SKIPPED:未设置 {COMPACT_SHOT}。");
|
||||
return;
|
||||
};
|
||||
let shot = Shot::load(&path);
|
||||
assert_eq!(shot.width, 736, "Compact 档那张必须是 736 宽");
|
||||
|
||||
let bands = bands(736.0);
|
||||
let ui = ui();
|
||||
let rail_left = bands.major_rail.0;
|
||||
let rail_right = rail_left + bands.major_rail.1 as usize;
|
||||
let center = rail_left + bands.major_rail.1 as usize / 2;
|
||||
// 栏的底色(`MaterialLayer::Card`):取栏内空白处,不取窗口画布
|
||||
let card = shot.at(rail_left + 2, 500);
|
||||
|
||||
// ① 首项的竖向量:40(图标按钮的边长)——文字行会是 44
|
||||
let (top, height) = shot
|
||||
.first_filled_band(center, 156, 300, card)
|
||||
.expect("主版本栏里应当有第一项");
|
||||
assert_eq!(
|
||||
height as f32,
|
||||
ui.metrics().control_height,
|
||||
"Compact 档主版本栏首项应当是 40 高的图标按钮(y={top}),实测 {height}px —— \
|
||||
回到「ListItem 文字行」就是 44"
|
||||
);
|
||||
|
||||
// ② 横向:40 宽的药丸,在 64 栏里居中(左右各 12)
|
||||
let (start, len) = shot.fill_run(top + height / 2, rail_left, rail_right, center, card);
|
||||
assert_eq!(
|
||||
(start, len as f32),
|
||||
(rail_left + 12, ui.metrics().control_height),
|
||||
"首项应当是 40 宽的居中药丸(栏 {rail_left}..{rail_right}),实测 x={start} 宽 {len} —— \
|
||||
64 宽的通栏行说明它又画成文字了"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user