fix(app-iced): 缩略图墙一行两格 + 副文案只留元数据(出图实测)
- 3 格装不下:详情列宽 = FrameMetrics::column ≤ 424(1180 窗宽),卡片内宽约 370, 3×160+2×12=504 会把第 3 格挤成一条竖条(实测图上『未生成预览』竖着排)。改成 2 格 = 332px,与同列元信息卡的两列栅格同宽; - 原因只进占位框(短句),副文案仍画『大小 · 时间』:160px 里塞「原因 + OS 原文」会摊成 三行,把墙变得比图还吵;原文留在 ScreenshotThumbFailure::detail 供排障; - 出图:DevDocs/artifacts/m251/saves-light.png / saves-dark.png(1180×1400, --software-renderer --screenshot,ULL_SAVES_FIXTURE=1)。
This commit is contained in:
1 parent
843f6d08f9
commit
0b6b8f0a62
4 files changed
+45
-36
No files matched your search
@@ -196,9 +196,9 @@ pub fn from_state(state: &SavesState, catalog: Catalog) -> Saves {
|
|||||||
/// | 快照里的状态 | 框里 | 副文案 |
|
/// | 快照里的状态 | 框里 | 副文案 |
|
||||||
/// |---|---|---|
|
/// |---|---|---|
|
||||||
/// | `Ready`(像素自洽) | 真图 | `大小 · 时间` |
|
/// | `Ready`(像素自洽) | 真图 | `大小 · 时间` |
|
||||||
/// | `Ready`(像素长度不对) | 「解不出这张图」 | 同一句 + 原因(防御性:壳层不该拿到坏像素) |
|
/// | `Ready`(像素长度不对) | 「解不出这张图」 | `大小 · 时间`(防御性:壳层不该拿到坏像素) |
|
||||||
/// | `NotGenerated` | 「未生成预览」 | 同一句(超预算没轮到) |
|
/// | `NotGenerated` | 「未生成预览」 | `大小 · 时间`(超预算没轮到) |
|
||||||
/// | `Failed` | 具名原因 | 原因 + OS/解码器原文(有的话) |
|
/// | `Failed` | 具名原因(太大 / 读不出 / 解不出) | `大小 · 时间`(原文留在 DTO 里,不上墙) |
|
||||||
fn shot_cell(shot: &ScreenshotView, texts: Texts) -> Shot {
|
fn shot_cell(shot: &ScreenshotView, texts: Texts) -> Shot {
|
||||||
let stamp = rows::shot_detail(shot.bytes, shot.modified_ms, texts);
|
let stamp = rows::shot_detail(shot.bytes, shot.modified_ms, texts);
|
||||||
match &shot.preview {
|
match &shot.preview {
|
||||||
@@ -212,31 +212,27 @@ fn shot_cell(shot: &ScreenshotView, texts: Texts) -> Shot {
|
|||||||
},
|
},
|
||||||
// 像素长度与宽高对不上:壳层不该拿到这种数据,但拿到了也不能画成花屏。
|
// 像素长度与宽高对不上:壳层不该拿到这种数据,但拿到了也不能画成花屏。
|
||||||
ScreenshotPreview::Ready(_) => {
|
ScreenshotPreview::Ready(_) => {
|
||||||
missing(shot, texts.saves_preview_reason(ScreenshotThumbKind::Decode), "")
|
missing(shot, texts.saves_preview_reason(ScreenshotThumbKind::Decode), stamp)
|
||||||
|
}
|
||||||
|
ScreenshotPreview::NotGenerated => {
|
||||||
|
missing(shot, texts.saves_preview_none(), stamp)
|
||||||
|
}
|
||||||
|
ScreenshotPreview::Failed(failure) => {
|
||||||
|
missing(shot, texts.saves_preview_reason(failure.kind), stamp)
|
||||||
}
|
}
|
||||||
ScreenshotPreview::NotGenerated => missing(shot, texts.saves_preview_none(), ""),
|
|
||||||
ScreenshotPreview::Failed(failure) => missing(
|
|
||||||
shot,
|
|
||||||
texts.saves_preview_reason(failure.kind),
|
|
||||||
failure.detail.as_str(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 「没有可画的像素」那一格:框里一句短话,格子下面把原因说全。
|
/// 「没有可画的像素」那一格:**框里说为什么**(短句),**格子下面照旧是大小与时间**。
|
||||||
///
|
///
|
||||||
/// 短话与副文案**分开**是刻意的:`160×90` 的框里塞不下 OS 原文,而原文正是用户能自己动手的
|
/// 为什么原因不进副文案:`160px` 宽的格子里,"原因 + OS 原文"会摊成三行,把墙变得比图还吵;
|
||||||
/// 那一半信息(权限 / 被占用 / 文件没了),所以它去副文案那一行(会换行,不会丢)。
|
/// 而 `ScreenshotThumbFailure::detail`(`9437184 > 8388608 字节` / `Access is denied.`)留在
|
||||||
/// **不靠字符串切分**去拆两句:切分遇到「原因本身带括号」就会切错(这条踩过)。
|
/// DTO 里,是排障用的原文,不是给墙看的正文。D54 第 6 条要的是「逐条、结构化、不是静默」——
|
||||||
fn missing(shot: &ScreenshotView, note: &str, detail: &str) -> Shot {
|
/// 那一句在框里,每条各说各的。
|
||||||
let caption = if detail.is_empty() {
|
fn missing(shot: &ScreenshotView, note: &str, stamp: String) -> Shot {
|
||||||
note.to_string()
|
|
||||||
} else {
|
|
||||||
format!("{note}({detail})")
|
|
||||||
};
|
|
||||||
Shot {
|
Shot {
|
||||||
name: shot.file.clone(),
|
name: shot.file.clone(),
|
||||||
detail: caption,
|
detail: stamp,
|
||||||
preview: ShotPreview::Missing {
|
preview: ShotPreview::Missing {
|
||||||
note: note.to_string(),
|
note: note.to_string(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,11 +23,19 @@
|
|||||||
//! 不拉伸(`ContentFit::Contain` 默认)。占位框反而钉死 `160×90`——它是"这里本该有一张图"的
|
//! 不拉伸(`ContentFit::Contain` 默认)。占位框反而钉死 `160×90`——它是"这里本该有一张图"的
|
||||||
//! 占位,尺寸要可预测,不能让一句话把它撑变形。
|
//! 占位,尺寸要可预测,不能让一句话把它撑变形。
|
||||||
//!
|
//!
|
||||||
//! ## 一行放几格
|
//! ## 一行放几格(这条是**量出来的**,不是拍的)
|
||||||
//!
|
//!
|
||||||
//! [`PER_ROW`] = 3:`3 × 160 + 2 × 12 = 504px`。详情列在 1180 宽的窗口里内宽约 700,放 4 格
|
//! 详情列的宽度由 `pages::layout` 的 `FrameMetrics::column` 定,而它 `≤ measure_cap_px()`
|
||||||
//! (676px)会贴边;窄窗口(<840 时详情整页替换)只剩约 500 内宽,3 格是**同时能站住两档**的
|
//! (1180 窗宽下 = 424,见 `layout.rs` 的文档)。卡片还有左右内边距 ⇒ 实际内宽约 **370**。
|
||||||
//! 那个数。改它之前先看 `DevDocs/artifacts/m251/` 的两张图。
|
//!
|
||||||
|
//! 于是:**3 格 = 3 × 160 + 2 × 12 = 504px 装不下**(M251 第一次出图实测:第 3 格被挤成一条
|
||||||
|
//! 竖条,`未生成预览` 四个字竖着排)。2 格 = `2 × 160 + 12 = 332px`,与同一列里元信息卡的
|
||||||
|
//! 两列栅格(`META_CARD_MIN_WIDTH × 2 + META_GAP = 332`)**同宽**——这不是巧合,是同一列里
|
||||||
|
//! 已经成立的节奏。
|
||||||
|
//!
|
||||||
|
//! 代价写在明处:60 张 = 30 行,要滚很久。换 3 列只能把格子缩到 ~112px(图更小),D54 的
|
||||||
|
//! `THUMBNAIL_MAX_EDGE = 160` 也就白解了;要真正做到「宽度自适应列数」得引一个栅格控件,
|
||||||
|
//! 那是新依赖,不在本 mission 的取舍里。
|
||||||
|
|
||||||
use iced::widget::{column, container, image, row};
|
use iced::widget::{column, container, image, row};
|
||||||
use iced::{Alignment, Background, Element, Length};
|
use iced::{Alignment, Background, Element, Length};
|
||||||
@@ -49,8 +57,8 @@ const THUMB_WIDTH: f32 = 160.0;
|
|||||||
/// 占位框的高(16:9,与常见的截图宽高比一致)。
|
/// 占位框的高(16:9,与常见的截图宽高比一致)。
|
||||||
const THUMB_HEIGHT: f32 = 90.0;
|
const THUMB_HEIGHT: f32 = 90.0;
|
||||||
|
|
||||||
/// 一行放几格(理由见模块文档)。
|
/// 一行放几格(= 同一列里元信息卡的两列栅格;理由见模块文档)。
|
||||||
const PER_ROW: usize = 3;
|
const PER_ROW: usize = 2;
|
||||||
|
|
||||||
/// 整面墙(`saves.rs` 的 `section` 调;空列表不该走到这里)。
|
/// 整面墙(`saves.rs` 的 `section` 调;空列表不该走到这里)。
|
||||||
pub(super) fn grid<'a>(ui: &'a Ui, shots: &'a [Shot]) -> Element<'a, Message> {
|
pub(super) fn grid<'a>(ui: &'a Ui, shots: &'a [Shot]) -> Element<'a, Message> {
|
||||||
|
|||||||
@@ -315,15 +315,17 @@ fn every_preview_state_gets_its_own_caption() {
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// 失败的副文案要带 OS/解码器原文(只进括号),没生成的那格只要一句短话。
|
// 副文案三格一样是「大小 · 时间」:原因在框里,不把墙变得比图还吵(原文留在 DTO 里)。
|
||||||
assert_eq!(
|
assert!(
|
||||||
saves.screenshots[2].detail,
|
saves.screenshots[1].detail.starts_with("4.0 KB"),
|
||||||
format!(
|
"没生成的那格也要有大小与时间:{:?}",
|
||||||
"{}(9437184 > 8388608 字节)",
|
saves.screenshots[1].detail
|
||||||
texts.saves_preview_reason(ScreenshotThumbKind::TooLarge)
|
);
|
||||||
)
|
assert!(
|
||||||
|
saves.screenshots[2].detail.starts_with("9.0 MB"),
|
||||||
|
"失败的那格同样保留元数据:{:?}",
|
||||||
|
saves.screenshots[2].detail
|
||||||
);
|
);
|
||||||
assert_eq!(saves.screenshots[1].detail, texts.saves_preview_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 像素长度与宽高对不上时**不许**建句柄(`Handle::from_rgba` 不校验长度 ⇒ 画出来是花屏)。
|
/// 像素长度与宽高对不上时**不许**建句柄(`Handle::from_rgba` 不校验长度 ⇒ 画出来是花屏)。
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ impl Texts {
|
|||||||
pub(in crate::pages::instances) fn saves_no_screenshots(self) -> &'static str {
|
pub(in crate::pages::instances) fn saves_no_screenshots(self) -> &'static str {
|
||||||
self.text(Key::InstancesSavesNoScreenshots)
|
self.text(Key::InstancesSavesNoScreenshots)
|
||||||
}
|
}
|
||||||
/// 「有图标」:只判 `icon.png` 存不存在(不解码,缩略图墙不在 U3 范围内)。
|
/// 「有图标」:只判 `icon.png` 存不存在。
|
||||||
|
///
|
||||||
|
/// M251 之后**仍未解**世界图标——缩略图墙只覆盖 `screenshots/`(D54 的范围)。这行字眼
|
||||||
|
/// 原来写的是「缩略图墙不在 U3 范围内」,那句话今天已经失真(墙做了),改成现状描述。
|
||||||
pub(in crate::pages::instances) fn saves_world_icon(self) -> &'static str {
|
pub(in crate::pages::instances) fn saves_world_icon(self) -> &'static str {
|
||||||
self.text(Key::InstancesSavesWorldIcon)
|
self.text(Key::InstancesSavesWorldIcon)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user