From a650f8184bc8582587b86586c8b7152912221484 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Mon, 2 Nov 2026 02:03:22 +0800 Subject: [PATCH] =?UTF-8?q?fix(app-iced):=20=E5=BF=85=E4=BF=AE2=20?= =?UTF-8?q?=E5=85=83=E4=BF=A1=E6=81=AF=E5=8D=A1=E6=9C=80=E5=B0=8F=E5=AE=BD?= =?UTF-8?q?=E6=94=B9=E7=94=B1=E5=86=85=E5=AE=B9=E6=8E=A8=EF=BC=88136=20?= =?UTF-8?q?=E2=86=92=20160=EF=BC=89=EF=BC=8C1180=20=E4=B8=8B=E7=BD=91?= =?UTF-8?q?=E6=A0=BC=202=20=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 视觉 r2 抓到的回归:上一轮为塞进三列把 `META_CARD_MIN_WIDTH` 收到 136,只算外框、忘了 `card_padding = 20`,卡内只剩 96px —— demo 里 `NeoForge 20.4.237`(实测 118px)与 `NeoForge 21.1.72`(110px)都会按词折成两行,同排另两张卡仍一行 → **卡底不齐**。 - 最小宽改由**卡内最长内容**反推:118(Segoe UI 14px 实测)+ 2×20(令牌 `card_padding`)= 158 → 取 4px 栅格 **160**(值上等于最初的 160,但这次是内容推出来的,不是凑上限凑出来的)。 - 于是元信息网格在 1180 宽落到 **2 列**(2×160 + 12 = 332;3 列要 504 > 行宽上限), 卡内可排 **166px** ≥ 118,所有值单行放下;6 格 = 3 行 2 列。 - 新增单测 `meta_card_is_wide_enough_for_the_longest_value`(此前 39 条 pages 测试没有一条覆盖它): ① 夹具 `NeoForge 20.4.237` 必须仍在 DEMO 里(数据变了就红,提醒重新量); ② `META_CARD_MIN_WIDTH − 2×card_padding ≥ 118`;③ 实际两列布局下每卡内宽也 ≥ 118。 - `meta_grid_drops_columns_as_the_detail_narrows` 跟着改成「上限处 2 列」(1180 与 900 同为 2 列, 差别落在详情列宽 435 vs 404)。 --- crates/app-iced/src/pages/instances/tests.rs | 35 ++++++++++++++++++++ crates/app-iced/src/pages/layout.rs | 18 +++++++--- crates/app-iced/src/pages/layout/tests.rs | 13 +++----- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/crates/app-iced/src/pages/instances/tests.rs b/crates/app-iced/src/pages/instances/tests.rs index 76c6015..f611bfd 100644 --- a/crates/app-iced/src/pages/instances/tests.rs +++ b/crates/app-iced/src/pages/instances/tests.rs @@ -15,6 +15,41 @@ fn demo_rows_overflow_the_visible_list() { ); } +/// 元信息卡必须装得下**卡内最长的那条值**(视觉 r2:136 漏算 padding,版本串折行让卡底不齐)。 +#[test] +fn meta_card_is_wide_enough_for_the_longest_value() { + use super::super::layout::{measure_cap_px, META_CARD_MIN_WIDTH, META_GAP}; + use unlucky_design::spacing::LayoutMetrics; + + // 实测(Segoe UI 14px):demo 数据里最宽的元信息值是加载器版本串 + const LONGEST_VALUE: &str = "NeoForge 20.4.237"; + const LONGEST_VALUE_PX: f32 = 118.0; + + let padding = LayoutMetrics::DESKTOP.card_padding; + assert_eq!(padding, 20.0, "卡内边距来自令牌"); + + // 夹具与 demo 数据不许脱节:这个值必须真在 DEMO 里 + assert!( + DEMO.iter().any(|entry| entry.loader == LONGEST_VALUE), + "夹具 {LONGEST_VALUE:?} 已经不在 DEMO 里了,请重新量最长值" + ); + + // ① 单卡外框减掉两侧内边距,必须放得下这条值(否则 text 会按词折行 → 卡底不齐) + let inner = META_CARD_MIN_WIDTH - 2.0 * padding; + assert!( + inner >= LONGEST_VALUE_PX, + "卡内可用 {inner}px 装不下 {LONGEST_VALUE_PX}px 的 {LONGEST_VALUE:?}(外框 {META_CARD_MIN_WIDTH} − 2×{padding})" + ); + + // ② 实际版面里每张卡的内宽(2 列时)也要够 + let two_columns = (measure_cap_px() - META_GAP) / 2.0; + assert!( + two_columns - 2.0 * padding >= LONGEST_VALUE_PX, + "两列布局下卡内可用 {}px 装不下 {LONGEST_VALUE_PX}px", + two_columns - 2.0 * padding + ); +} + #[test] fn every_demo_entry_has_name_version_and_loader() { for entry in &DEMO { diff --git a/crates/app-iced/src/pages/layout.rs b/crates/app-iced/src/pages/layout.rs index 8019c54..f3fd33a 100644 --- a/crates/app-iced/src/pages/layout.rs +++ b/crates/app-iced/src/pages/layout.rs @@ -99,11 +99,19 @@ pub const FORM_MIN_WIDTH: f32 = DETAIL_MIN_WIDTH; /// 两列之间的间隙(`Space::X6` = 24)。 pub const PANES_GAP: f32 = Space::X6.dp(); -/// 详情里元信息网格的**单卡最小宽度**:三列要塞进 435 的行宽上限 → 3×136 + 2×12 = **432** ≤ 435。 +/// 详情里元信息网格的**单卡最小宽度 = 由卡内最长内容反推**:**160**。 /// -/// 这一个是跟着行宽上限走的:上限从 504 收到 435 之后,旧的 160 会让三列塞不下 -/// (3×160 + 24 = 504 > 435),所以同步收窄到 136。四列(4×136 + 36 = 580)从来就没进过上限。 -pub const META_CARD_MIN_WIDTH: f32 = 136.0; +/// 推导(不是凑出来的): +/// - 卡内最长值是 demo 里的 `NeoForge 20.4.237`,Segoe UI 14px 实测 **118px**; +/// - 卡两侧内边距 `LayoutMetrics::card_padding` = 20,故最小外框 = 118 + 2×20 = **158**; +/// - 取 4px 栅格的 **160**(等价于旧值,但这次是**内容推出来的**,不是凑上限凑出来的)。 +/// +/// r2 复审抓到的回归:上一轮为了塞三列把这里收到 136,忘了算 padding —— +/// 卡内只剩 96px,`NeoForge 20.4.237`(118px)与 `NeoForge 21.1.72`(110px)都会折成两行, +/// 同排另两张卡仍一行 → **卡底不齐**。160 之下 3 列要 3×160 + 2×12 = 504,超出行宽上限, +/// 所以元信息网格在 1180 宽是 **2 列**(2×160 + 12 = 332,卡内可排 166px,装得下 118)。 +/// 这也是有意的:mod 加载器版本串只会更长,2 列宽卡比 3 列挤卡更抗将来。 +pub const META_CARD_MIN_WIDTH: f32 = 160.0; /// 元信息网格卡片间距(`Space::X3` = 12)。 pub const META_GAP: f32 = Space::X3.dp(); @@ -269,7 +277,7 @@ pub fn content_column_width(window_width: f32) -> f32 { available_width(window_width).min(measure_cap_px()) } -/// 元信息网格的列数:3 列需要 432(≤ 行宽上限 435),2 列需要 284,否则 1 列。 +/// 元信息网格的列数:3 列需要 504(超出行宽上限,1180 下用不到),2 列需要 332,否则 1 列。 pub fn meta_columns(width: f32) -> usize { for columns in [3usize, 2] { let needed = columns as f32 * META_CARD_MIN_WIDTH + (columns - 1) as f32 * META_GAP; diff --git a/crates/app-iced/src/pages/layout/tests.rs b/crates/app-iced/src/pages/layout/tests.rs index 97f8adf..e0e16fb 100644 --- a/crates/app-iced/src/pages/layout/tests.rs +++ b/crates/app-iced/src/pages/layout/tests.rs @@ -288,18 +288,15 @@ fn settings_pane_keeps_the_drawer_width_and_a_readable_form_column() { #[test] fn meta_grid_drops_columns_as_the_detail_narrows() { - assert_eq!( - meta_columns(measure_cap_px()), - 3, - "432 = 3×136 + 2×12 ≤ 435" - ); + // 160 宽卡:3 列要 3×160 + 2×12 = 504(超出行宽上限),所以上限处就是 2 列;2 列要 332 + assert_eq!(meta_columns(measure_cap_px()), 2, "2×160 + 12 = 332"); assert_eq!(meta_columns(340.0), 2); - assert_eq!(meta_columns(280.0), 1); + assert_eq!(meta_columns(330.0), 1); - // 与三张对照图的详情列宽对应:1180 → 3 列,900 → 2 列 + // 1180 与 900 都是 2 列(160 宽卡三列塞不进行宽上限),差别落在详情列宽 let wide = frame_metrics(PaneKit::ListDetail, 1180.0); let mid = frame_metrics(PaneKit::ListDetail, 900.0); - assert_eq!(wide.meta_columns, 3); + assert_eq!(wide.meta_columns, 2); assert_eq!(mid.meta_columns, 2); assert!(mid.column < wide.column, "900 宽下详情列必须真的变窄"); }