diff --git a/crates/app-iced/src/pages/download.rs b/crates/app-iced/src/pages/download.rs index a928caa..af799fd 100644 --- a/crates/app-iced/src/pages/download.rs +++ b/crates/app-iced/src/pages/download.rs @@ -228,7 +228,12 @@ fn body<'a>( column![ rails::stacked_chips(ui, catalog, model), divider::full(ui), - rows::content(ui, catalog, model, rows::TailShare::of(rails.tier)), + rows::content( + ui, + catalog, + model, + rows::TailShare::of(rails.tier, &install::panel_state(model)), + ), ] .spacing(ui.space(Space::X2)) .width(Length::Fill) diff --git a/crates/app-iced/src/pages/download/install.rs b/crates/app-iced/src/pages/download/install.rs index ec6e124..c656643 100644 --- a/crates/app-iced/src/pages/download/install.rs +++ b/crates/app-iced/src/pages/download/install.rs @@ -11,7 +11,25 @@ //! //! **M125 的例外**:chips 档(窗口 < 736,竖栏退回顶部 chip 行)里这块面板太贵——它按内容高 //! 钉死会把版本列表压成 18px 的缝。那一档改成「列表与面板各吃一半、面板自己可滚」 -//! (`rows::TailShare::Shared`),所以「钉在底部」只对三栏档成立。 +//! (`rows::TailShare::Shared`)——但只在**面板真的占高度**时(`Options` / `Install`); +//! 只画一行提示的 `Hint` 态仍钉死,否则 8 个版本的列表会被砍到 4 行(r1 复审 P2-1)。 +//! +//! ## 已知风险:chips 档最多会同时出现**三层**滚动(r1 复审 P3-2,未验) +//! +//! ```text +//! 版本行列表 scrollable ← 第 1 层(任何档位都在) +//! └ 面板 scrollable(chips 档才有) ← 第 2 层 +//! └ 加载器版本 menu 的 scrollable ← 第 3 层(`options/view.rs` 的 `VERSION_ROWS`) +//! ``` +//! +//! **鼠标落在第 3 层上滚轮会不会冒泡到第 2 层,本仓没有实测通道**(静态图拍不到滚动嵌套)。 +//! iced 的 `scrollable` 在指针悬停于内层时由内层消费滚动,所以「滚到底之后继续滚会带动外层」 +//! 属于**未验证行为**;两个方向的坏结果分别是「面板卡住滚不动」与「一滚就跳到外层」。 +//! +//! 缓解方案(本轮**没做**,理由:不是一处小改)——把「实例名」那一格从面板的滚动区里移出去, +//! 做成面板的固定页脚:那要把 `options::section` 拆成「可滚的上半块 + 钉死的下半块」, +//! 动 `options/view.rs` / `install.rs` / `rows.rs` 三处,而且改完仍然**无法**验证滚轮冒泡 +//! (没有通道),只是把风险从「某一格被挡」换成「面板永不挡那一格」。所以先登记为已知风险。 //! //! ## 只有意图,没有乐观回显 //! diff --git a/crates/app-iced/src/pages/download/rails.rs b/crates/app-iced/src/pages/download/rails.rs index fb5cf38..0c35a46 100644 --- a/crates/app-iced/src/pages/download/rails.rs +++ b/crates/app-iced/src/pages/download/rails.rs @@ -91,7 +91,7 @@ pub(super) fn three_column<'a>( ui, catalog, model, - rows::TailShare::of(rails.tier), + rows::TailShare::of(rails.tier, &super::install::panel_state(model)), )) .width(Length::Fixed(rails.list)) .height(Length::Fill), diff --git a/crates/app-iced/src/pages/download/rails/major.rs b/crates/app-iced/src/pages/download/rails/major.rs index 11a7064..4bb7eff 100644 --- a/crates/app-iced/src/pages/download/rails/major.rs +++ b/crates/app-iced/src/pages/download/rails/major.rs @@ -11,15 +11,17 @@ use unlucky_design::spacing::Space; use unlucky_i18n::{Catalog, Key}; +use unlucky_design::icons::IconKind; + use crate::app::components::navigation::list_item::{list_item, ListItem}; use crate::pages::download::group; -use crate::pages::download::model::{Category, Download}; +use crate::pages::download::model::Download; use crate::pages::frame; use crate::pages::layout::RAIL_ICON_WIDTH; use crate::pages::Message; use crate::theme::Ui; -use super::{category_icon, labeled, rail_title}; +use super::{labeled, rail_title}; /// 第二栏:主版本桶(第一项永远是「全部版本」;没有主版本概念的版本不单独占一项)。 pub(in crate::pages::download) fn major_rail<'a>( @@ -77,19 +79,32 @@ pub(in crate::pages::download) enum MajorShape<'a> { /// `ListItem` 两侧内边距各 16(`pad_start` / `pad_end`),所以 64 宽的栏里文本格 /// = 64 − 32 = **32**,再长就被 `clip(true)` 切掉。`BodyLarge` 16px 下: /// -/// | 标签 | 墨迹宽 | 32 里放得下? | +/// | 标签 | 墨迹(736×760 实测) | 32 的文本格里放得下? | /// |---|---|---| -/// | `1.21` | 26(736×760 那张实测) | ✓ 余量 6 | -/// | `24w` | 26(同上) | ✓ 余量 6 | -/// | `All versions` | ≈84(12 字符,按 0.4em 下限也有 77) | ✗ 只画得出「All v」(缺陷 2 的原图) | -/// | `全部版本` | 4 × 16 = **64**(全角 1em,精确值) | ✗ 只画得出「全部」,而且正好顶到裁切边 | +/// | `1.21` | 26px(x=217..242) | ✓ 余量 **6** | +/// | `1.20` | 28px(x=217..244) | ✓ 余量 **4** | +/// | `24w` | 29px(x=216..244) | ✓ 余量 **3**(最紧的一条) | +/// | `All versions` | 墨迹画到 x=247,裁切边在 248 | ✗ 被切:实际只画出「All v」(缺陷 2 的原图) | +/// | `全部版本` | 4 全角字 × 16 = **64**(精确值,不是估计) | ✗ 只画得出「全部」,且顶到裁切边 | /// -/// 短标签这条退路也不行:中文「全部」= 2 × 16 = **32**,与文本格**一样宽、零余量**, -/// 「裁不裁」取决于字体渲染的小数位——一个查不出、也守不住的边界。所以图标条档的 -/// 「全部版本」**不画文字**:换成与本页第一级那个「全部」同一个网格图标 -/// ([`category_icon`] 的 `Category::All`),全称进 tooltip(宽度不设限,中英文都完整)。 +/// 桶标签的余量是 **3~6px**(视字形:`24w` 最紧 3,`1.21` 最松 6)。短标签「全部」= 2 × 16 = **32**, +/// 与文本格**一样宽、零余量**——比桶标签那 3~6px 更薄,裁不裁取决于字体渲染的小数位, +/// 是一个查不出也守不住的边界,所以「换短标签」这条退路不采用。于是图标条档的 +/// 「全部版本」**不画文字**:换成图标(字形见 [`major_shape`] 的下文), +/// 全称进 tooltip(宽度不设限,中英文都完整)。 /// /// 桶标签本来就是 3~5 字符的短串,两档都照旧画原文。 +/// +/// ## 用哪个字形(r1 复审 P3-1) +/// +/// 这里用 **`IconKind::Logs`(文档 + 正文行)**,不用第一级「全部类型」那个网格 +/// (`category_icon(Category::All)`):Compact 档两条 64 宽的栏在屏上相邻,若两级都用网格 + +/// 同一个 40×40 `Tonal` 药丸,**第一级「全部」与第二级「全部版本」像素级同形**, +/// 只能靠 hover 出 tooltip 才分得清是哪个轴。文档/清单字形读作「整份版本清单」, +/// 与网格(「全部类型」)分得开。 +/// +/// 更贴切的「版本 / 历史 / 层叠」字形本仓没有(图标语汇只有 28 个描边字形,登记在 +/// `unlucky_design::icons`),新增字形要动 `crates/design`(超出本页范围),此处按既有字形借用。 pub(in crate::pages::download) const fn major_shape( entry: MajorEntry<'_>, icon_only: bool, @@ -146,17 +161,13 @@ fn major_item<'a>(ui: &'a Ui, entry: MajorEntry<'a>, icon_only: bool) -> Element } // 图标条档的「全部版本」:图标(与本页第一级的「全部」同一个网格)+ 全称 tooltip。 // 高度对齐 `ListItem` 的行高,两档的行节奏才一样。 - MajorShape::Icon(full) => container(labeled( - ui, - full, - category_icon(Category::All), - entry.selected, - press, - )) - .width(Length::Fill) - .height(Length::Fixed(ui.metrics().row_height)) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .into(), + MajorShape::Icon(full) => { + container(labeled(ui, full, IconKind::Logs, entry.selected, press)) + .width(Length::Fill) + .height(Length::Fixed(ui.metrics().row_height)) + .align_x(Alignment::Center) + .align_y(Alignment::Center) + .into() + } } } diff --git a/crates/app-iced/src/pages/download/rows.rs b/crates/app-iced/src/pages/download/rows.rs index 695f9fc..553f65e 100644 --- a/crates/app-iced/src/pages/download/rows.rs +++ b/crates/app-iced/src/pages/download/rows.rs @@ -28,9 +28,11 @@ //! 固定 ≈316px(版本行 40 + 标题 16 + 五个 loader chip 34 + 标题 16 + 菜单 112 + 实例名 56)。 //! 同样的 760 高窗口里:三栏档列表还剩 158px(3 整行),chips 档只剩 18px。 //! -//! 修法是 [`TailShare`] 两档:三栏档照旧钉死(面板整块在屏内),chips 档改成两半各 `Fill` -//! ——列表拿到 ≈182px(**4 整行**),面板拿到同样一半、**自己可滚**(顶端那个实心「安装」 -//! 按钮与五个 loader chip 仍在屏内,实例名那一格滚一下就到)。 +//! 修法是 [`TailShare`] 两档:三栏档照旧钉死(面板整块在屏内),chips 档**且面板真的占高度** +//! (选项区展开 / 安装中)时改成两半各 `Fill`——列表拿到 ≈182px(**4 整行**),面板拿到同样一半、 +//! **自己可滚**(顶端那个实心「安装」按钮与五个 loader chip 仍在屏内,实例名那一格滚一下就到)。 +//! 「刚进页面还没选版本」(`Hint`,面板只有一行提示)**不共享**:那一态 8 个版本能全在屏, +//! 五五分会砍成 4 行、下面留 ≈160px 空白(r1 复审 P2-1 的回归)。 //! //! 这条判据按**档位**而不是按高度:页面的模型里只有 `window_width`,窗口高度没接进页面, //! 所以「760 高到底够不够」在运行时是未知量(边界写在 [`TailShare`] 的文档里)。 @@ -70,8 +72,9 @@ use super::super::frame; use super::super::layout::rails::{LIST_DATE_WIDTH, LIST_KIND_WIDTH, ROW_GAP, ROW_ICON_BUTTON}; use super::super::layout::VersionRailTier; use super::super::Message; +use super::install::{self, PanelState}; use super::model::{Download, VersionRow}; -use super::{install, states}; +use super::states; /// 列表列(三栏档与 chips 档共用一份)。 /// @@ -129,7 +132,8 @@ pub(super) fn content<'a>( /// | 档 | 分法 | 为什么 | /// |---|---|---| /// | `Wide` / `Narrow` / `Compact` | [`TailShare::Pinned`]:面板按内容高钉在底部,列表吃剩下的 | 竖栏档在 760 高的窗口里列表还剩 158px(3 整行),面板整块在屏内——两样都对 | -/// | `Chips` | [`TailShare::Shared`]:两半各 `Fill`,面板自己可滚 | chips 档比三栏档多花两行 chip(tabs 48 + chips 48 + 间距),列表只剩 **18px**(M123 实测 0.4 行),面板 316px 全在屏内也没用——版本号读不出来就换不了版本 | +/// | `Chips` + 面板**占高度**(选项区展开 / 安装中) | [`TailShare::Shared`]:两半各 `Fill`,面板自己可滚 | chips 档比三栏档多花两行 chip(tabs 48 + chips 48 + 间距),列表只剩 **18px**(M123 实测 0.4 行),面板 316px 全在屏内也没用——版本号读不出来就换不了版本 | +/// | 任何档 + 只画 `Hint`(没选版本,面板只有一行) | [`TailShare::Pinned`] | 一行提示不值得对半分:700×760 下 8 个版本能全在屏,对半分只剩 4 个、下面留 160px 空白(r1 复审 P2-1) | /// /// 判据是**档位**不是高度:页面的模型里只有 `window_width`(`pages/model.rs`),窗口高度 /// 没有接进页面(要接得改 `pages/mod.rs` 的入口签名,超出本页的边界)。所以这里按「哪一档 @@ -143,13 +147,21 @@ pub(super) enum TailShare { } impl TailShare { - /// 档位 → 分高方式(纯函数,单测直接钉它)。 - pub(super) const fn of(tier: VersionRailTier) -> Self { - match tier { - VersionRailTier::Chips => Self::Shared, - VersionRailTier::Wide | VersionRailTier::Narrow | VersionRailTier::Compact => { - Self::Pinned + /// 档位 + **面板形态** → 分高方式(纯函数,单测直接钉它)。 + /// + /// 两个条件必须同时成立才共享: + /// 1. 档位是 `Chips`(多花两行 chip,竖排预算最紧的那一档); + /// 2. 面板真的**占高度**——`Options`(选中了版本、选项区展开)或 `Install`(进度 / 失败)。 + /// + /// `Hint`(刚进页面还没选版本)只是**一行**提示(≈30px),对半分会让 8 个版本只显示 4 个、 + /// 下半块留 ≈160px 空画布(r1 复审 P2-1 实测:修前 700×760 下 8 行全在屏,只判档位的写法 + /// 把它压到 4 行)。所以 `Hint` **在任何档位都钉死**。 + pub(super) const fn of(tier: VersionRailTier, state: &PanelState<'_>) -> Self { + match (tier, state) { + (VersionRailTier::Chips, PanelState::Options(_) | PanelState::Install(_)) => { + Self::Shared } + _ => Self::Pinned, } } } diff --git a/crates/app-iced/src/pages/download/tests/options_shot.rs b/crates/app-iced/src/pages/download/tests/options_shot.rs index c1bbba0..d3d26c8 100644 --- a/crates/app-iced/src/pages/download/tests/options_shot.rs +++ b/crates/app-iced/src/pages/download/tests/options_shot.rs @@ -41,6 +41,8 @@ const WIDE_SHOT: &str = "ULL_DL_OPTIONS_SHOT"; 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"; +/// 700 宽、**不设 `ULL_DL_DEMO`**(= `ready`,还没选版本)那张——r1 复审 P2-1 的守门用。 +const READY_SHOT: &str = "ULL_DL_READY_SHOT"; fn ui() -> Ui { Ui::with_platform(SchemeFlavor::Light, Platform::Windows, FAMILIES) @@ -183,6 +185,34 @@ impl Shot { (start, end - start + 1) } + /// `[from, to)` 里在 `[left, right)` 有墨迹的**条数**(两条之间空 ≥ 8 行才算两条)。 + /// + /// 用它数「读得出几个版本号」:一行版本号的文本墨迹约 11px 高、行距 44,中间空 30+ ⇒ + /// 一条对应一行。被压扁时整块只剩一条。 + fn ink_bands( + &self, + left: usize, + right: usize, + from: usize, + to: usize, + background: [u8; 3], + ) -> usize { + let mut bands = 0; + let mut gap = usize::MAX; + for y in from..to.min(self.height) { + let inked = (left..right).any(|x| self.at(x, y) != background); + if inked { + if gap >= 8 { + bands += 1; + } + gap = 0; + } else { + gap = gap.saturating_add(1); + } + } + bands + } + /// 列表列里、`from` 起的**第一条薄通栏横线**的 y(`divider::full` 画的那条 1px 线)。 /// /// 用它量「版本列表到哪儿为止」:尾部面板上沿有一条通栏分隔线,它上面的高度就是列表拿到的。 @@ -350,3 +380,5 @@ fn an_options_shot_shows_both_rails_and_the_whole_panel() { mod chips; mod compact; + +mod ready; diff --git a/crates/app-iced/src/pages/download/tests/options_shot/compact.rs b/crates/app-iced/src/pages/download/tests/options_shot/compact.rs index 5fb4afe..d1e30ed 100644 --- a/crates/app-iced/src/pages/download/tests/options_shot/compact.rs +++ b/crates/app-iced/src/pages/download/tests/options_shot/compact.rs @@ -4,12 +4,22 @@ use super::{bands, shot_path, ui, Shot, COMPACT_SHOT}; /// `04-…-compact736.png`(736×760,`ULL_DL_COMPACT_SHOT`)——**Compact 档主版本栏(缺陷 2)**。 /// -/// 修前(M123 取证):主版本栏宽 64,`ListItem` 两侧内边距各 16 ⇒ 文本格 32, -/// 而首项「All versions」要 ≈84 ⇒ 图上只剩「**All v**」。中文更糟:「全部版本」= 4 全角字 = 64, -/// 32 里只剩「全部」而且正好顶到裁切边。 +/// 修前(M123 取证;本文件作者在 736×760 那张图上逐项量过):主版本栏宽 64,`ListItem` +/// 两侧内边距各 16 ⇒ 文本格 **32**(x=216..247,裁切边 248): +/// +/// | 标签 | 墨迹 | 结论 | +/// |---|---|---| +/// | `1.21` | 26px | 完整(余 6) | +/// | `1.20` | 28px | 完整(余 4) | +/// | `24w` | 29px | 完整(余 3,最紧) | +/// | `All versions` | 画到 x=247(裁切边 248) | **被切**:只剩「All v」 | +/// | `全部版本` | 4 全角字 = 64 > 32 | **被切**:只剩「全部」且顶到边 | +/// +/// 即桶标签的余量是 **3~6px**,而「全部版本」这条根本放不下。 /// /// 修后:图标条档的「全部版本」**不画文字**(`rails::major_shape` 的 `Icon` 分支)—— -/// 画成与本页第一级那个「全部」同一个网格图标(40×40 `Tonal` 药丸 + 全称 tooltip)。 +/// 画成文档/清单字形(40×40 `Tonal` 药丸 + 全称 tooltip)。**不是**第一级那个网格: +/// 两条 64 栏相邻,同字形 + 同药丸会让「全部类型」与「全部版本」像素级同形(r1 复审 P3-1)。 /// /// 抓得住:①首项是 **40×40 的图标药丸**(不是 64 宽 × 44 高的通栏文字行)——谁把它改回 /// 「画文字」(全称或短标签都会是通栏 44 行)这条立刻红;②药丸在 64 栏里**居中**(左右各 12)。 @@ -17,8 +27,9 @@ use super::{bands, shot_path, ui, Shot, COMPACT_SHOT}; /// /// ## 为什么不用「文字墨迹右边界 ≤ 栏右内边界」这条(复审建议的那条) /// -/// 它**在坏图上也是绿的**:修前那张图实测首项文字墨迹 x=216..244,而栏右内边界是 -/// `200 + 64 − 16 = 248` ⇒ 244 ≤ 248 成立——「All **v**」明明被裁掉了,断言却过。 +/// 它**在坏图上也是绿的**:修前那张图实测首项文字墨迹 x=216..247,而栏右内边界是 +/// `200 + 64 − 16 = 248` ⇒ 247 ≤ 248 成立(只剩 1px 余量)——「All **v**」明明被裁掉了, +/// 断言却过。这条比 r1 报告里写的更极端:差 1px 而不是 4px。 /// 原因是 `ListItem` 的 `clip(true)` 让墨迹**永远**停在裁切边之前,边界的比较量不出「被裁」。 /// 所以这里改钉**形状**:文字行是 64×44 的通栏带,图标按钮是 40×40 的居中药丸,两者像素可分。 /// diff --git a/crates/app-iced/src/pages/download/tests/options_shot/ready.rs b/crates/app-iced/src/pages/download/tests/options_shot/ready.rs new file mode 100644 index 0000000..1235051 --- /dev/null +++ b/crates/app-iced/src/pages/download/tests/options_shot/ready.rs @@ -0,0 +1,79 @@ +//! 「还没选版本」那一态(`ULL_DL_DEMO` 不设 = `ready`)的像素守门:r1 复审 P2-1。 +//! +//! ## 为什么要单独守这一态 +//! +//! M125 r1 的 `TailShare::of` **只判档位**,于是 chips 档的「刚进页面」也被五五分: +//! 面板里只有一行提示(`PanelState::Hint`,≈30px),却拿走一半高度 ⇒ 700×760 下 +//! 8 个版本只显示 **4** 个、下半块留 ≈160px 空画布(修前是 8 行全在屏)。 +//! 同目录 `chips.rs` 只拍「选中 + 选项区展开」,正是这个盲区把回归放了过去。 +//! +//! 现在的判据:`Chips` **且**面板真占高度(`Options` / `Install`)才共享; +//! `Hint` 在任何档位都钉死(见 `rows::TailShare::of`)。 +//! +//! ```text +//! # 不设 ULL_DL_DEMO(= ready)出 700×760 那张 +//! cargo run -p unlucky-app-iced --example pages_demo -- \ +//! --screen=download --width=700 --height=760 --screenshot=ready700.png \ +//! --screenshot-theme=light --software-renderer +//! ULL_DL_READY_SHOT=ready700.png cargo test -p unlucky-app-iced --lib options_shot +//! ``` +//! +//! 抓得住:①列表区 **≥ 6 整行**(数版本号那列的墨迹条数,行距 44);②尾部(提示那条 +//! 分隔线)在 6 行之后——五五分时它落在第 4 行下方,两条都立刻红。 +//! 抓不住:提示文案本身、hover/tooltip 层(静态图拍不到);也不管「选中且展开」那一态 +//!(那是 `chips.rs` 的活)。 + +use super::{bands, shot_path, ui, Shot, READY_SHOT}; + +/// `ready` 那一态(700×760):8 个版本里至少 6 行要在屏,尾部提示不许把列表压扁。 +/// +/// 修前实测(M125 r1,`TailShare::of` 只判档位的版本):列表区 `374..559` = 185px = 4 行, +/// 版本号那列只有 4 条墨迹;面板那半块 185px 里只有一行 `Pick a version, then install`, +/// 其余是空白 —— 这条断言在那种图上是红的(4 < 6)。 +#[test] +fn a_ready_shot_keeps_the_whole_visible_list() { + let Some(path) = shot_path(READY_SHOT) else { + eprintln!("[options-shot] SKIPPED:未设置 {READY_SHOT}。"); + return; + }; + let shot = Shot::load(&path); + assert_eq!(shot.width, 700, "这条只认 700×760 那张(chips 档)"); + + let bands = bands(700.0); + let canvas = shot.at(bands.list.0 + 2, 12); + let metrics = ui().metrics(); + let right = bands.list.0 + bands.list.1 as usize - 2; + + // ① 第一行版本号的墨迹带(ready 态没有选中行,所以量的是**文本**而不是底色; + // x 取 `+100` 落在版本号那几个字上——`+60` 会落进「前置下载图标」与文字之间的空档) + let (first_ink, _) = shot + .first_filled_band(bands.list.0 + 100, 360, 440, canvas) + .expect("列表列里应当有版本行"); + + // ② 尾部(提示)上沿的分隔线:它上面的高度才是列表拿到的 + let rule = shot + .first_rule_y(bands.list.0, right, first_ink, canvas) + .expect("找不到尾部那条分隔线(列表区的下界量不出来)"); + + // ③ 数得出几个版本号:8 行里至少 6 行在屏 + let rows = shot.ink_bands( + bands.list.0 + 50, + bands.list.0 + 140, + first_ink, + rule, + canvas, + ); + assert!( + rows >= 6, + "ready 态只读得出 {rows} 个版本号(要求 ≥ 6 个,演示数据共 8 个):\ + 面板那一行提示把列表对半切了(列表区 y={first_ink}..{rule},\ + 共 {}px,行高 {})", + rule - first_ink, + metrics.row_height + ); + assert!( + (rule - first_ink) as f32 >= 6.0 * metrics.row_height - metrics.row_height / 2.0, + "ready 态的列表区只有 {}px(y={first_ink}..{rule}),不足 6 整行", + rule - first_ink + ); +} diff --git a/crates/app-iced/src/pages/download/tests/view.rs b/crates/app-iced/src/pages/download/tests/view.rs index ecb2410..9a6a781 100644 --- a/crates/app-iced/src/pages/download/tests/view.rs +++ b/crates/app-iced/src/pages/download/tests/view.rs @@ -146,37 +146,94 @@ fn the_demo_data_is_sane() { assert!(demo.rows.iter().all(|row| !row.supporting.contains('版'))); } -/// 列表与尾部面板怎么分高,跟着**档位**走(M125 缺陷 1 的决策面)。 +/// 列表与尾部面板怎么分高:**档位 × 面板形态**两个条件同时成立才对半分。 /// -/// 修前:面板一律按内容高钉在底部,700×760 的 chips 档里列表只剩 18px(0.4 行)。 -/// 修后:chips 档两半各 `Fill`(面板自己可滚),三栏三档照旧钉死(760 高下列表还有 3 整行、 -/// 面板整块在屏内,两样都对)。 +/// 修前(M123):面板一律按内容高钉在底部,700×760 的 chips 档里列表只剩 18px(0.4 行)。 +/// M125 r1 修法只判档位,又踩出另一条回归:chips 档「刚进页面还没选版本」(`Hint`,面板只有 +/// 一行提示)也被五五分,700×760 下 8 个版本只显示 4 个、下半块留 ≈160px 空画布 +/// (复审 P2-1 实测,修前是 8 行全在屏)。 /// -/// 抓得住:有人把 chips 档改回 `Pinned`(700 下的像素守门 `a_chips_tier_shot_…` 立刻红); -/// 或者把三栏档也改成 `Shared`(1180 下选项面板被切成一半,`an_options_shot_…` 会红)。 -/// 抓不住:**实际分到多少像素**(`iced` 的 `Element` 没有几何内省,那只能出图量, -/// 见 `options_shot` 的像素断言与报告里的数值表)。 +/// 现在的判据:`Chips` **且**面板真占高度(`Options` / `Install`)才 `Shared`; +/// `Hint` 在任何档位都 `Pinned`。 +/// +/// 抓得住:①chips 档改回「只判档位」(`Hint` 跟着对半分 ⇒ 列表掉到 4 行,像素守门 +/// `ready::a_ready_shot_keeps_the_whole_visible_list` 立刻红);②三栏档也改成 `Shared` +/// (1180 下选项面板被切成一半,`an_options_shot_…` 会红)。 +/// 抓不住:**实际分到多少像素**(`iced` 的 `Element` 没有几何内省,那只能出图量)。 #[test] -fn the_chips_tier_shares_the_height_and_the_rails_tiers_pin_it() { +fn the_chips_tier_shares_the_height_only_when_the_panel_takes_room() { + use crate::pages::download::install::panel_state; + use crate::pages::download::options::{InstallDraft, Loader, OptionsView}; use crate::pages::download::rows::TailShare; use crate::pages::layout::VersionRailTier; + use unlucky_presenter::state::AppState; - assert_eq!(TailShare::of(VersionRailTier::Chips), TailShare::Shared); + let catalog = Lang::En.catalog(); + let mut app = AppState::new(); + app.apply(unlucky_presenter::dto::AppEvent::Core( + unlucky_presenter::dto::CoreEvent::VersionListChanged(vec![version( + "1.21.9", + VersionKind::Release, + false, + )]), + )); + + let row = VersionRow::of(&version("1.21.9", VersionKind::Release, false), catalog); + // 选中版本 ⇒ 选项区(面板真的占高度) + let options = Download { + options: Some(OptionsView::of( + &row, + &app, + &InstallDraft { + selected: Some("1.21.9".to_string()), + loader: Loader::Vanilla, + loader_version: String::new(), + instance_name: String::new(), + }, + catalog, + )), + install: None, + loaded: true, + ..Download::default() + }; + // 刚进页面 ⇒ 只有一行提示 + let hint = Download { + options: None, + install: None, + loaded: true, + ..Download::default() + }; + + assert_eq!( + TailShare::of(VersionRailTier::Chips, &panel_state(&options)), + TailShare::Shared, + "chips 档 + 选项区展开:对半分(列表 4 整行、面板自己可滚)" + ); + assert_eq!( + TailShare::of(VersionRailTier::Chips, &panel_state(&hint)), + TailShare::Pinned, + "chips 档 + 只有一行提示:钉死(8 个版本要在屏)" + ); for tier in [ VersionRailTier::Wide, VersionRailTier::Narrow, VersionRailTier::Compact, ] { - assert_eq!(TailShare::of(tier), TailShare::Pinned, "{tier:?}"); + assert_eq!( + TailShare::of(tier, &panel_state(&options)), + TailShare::Pinned, + "{tier:?}:三栏档一律钉死(面板整块在屏)" + ); + assert_eq!(TailShare::of(tier, &panel_state(&hint)), TailShare::Pinned); } - // 两个装配点(三栏装配 / chips 分支)都**按档位**传,不许在某一处写死一个字面量 + // 两个装配点(三栏装配 / chips 分支)都**按档位 + 面板形态**传 let chips_branch = include_str!("../../download.rs"); let three_column = include_str!("../rails.rs"); assert_eq!( - chips_branch.matches("TailShare::of(rails.tier)").count() - + three_column.matches("TailShare::of(rails.tier)").count(), + chips_branch.matches("TailShare::of(rails.tier,").count() + + three_column.matches("TailShare::of(rails.tier,").count(), 2, - "三栏装配与 chips 分支都要按档位传 TailShare" + "三栏装配与 chips 分支都要把面板形态传进 TailShare::of" ); }