Files
Unlucky-Launcher/crates/app-iced/src/app/tests/theme.rs
T
AskaEth 8958b7f67d fix(app-iced): IconButton Outlined 真的画环 + ELEVATED CARD 值串单行(M229 复审 P1)
P1-1:`icon()` 把 `StyleSpec.stroke` 写死 `0.0`,`IconVariant::Outlined` 的描边角色只到
`roles()` 为止、没画出来 —— Outlined 与 Plain 逐像素相同。改成「有描边角色就是 1px」,
并把「角色 → 填充/内容/描边」抽成 `icon_style_spec()`(`icon()` 直接吃它),单测因此能断言
**接线**而不只是断言 `icon_stroke()` 这个纯函数(第一版正是这样漏掉调用点的)。

回归门两条:纯函数/接线(`buttons/tests/icon.rs`,变异回 0.0 时红)+ 渲染层
(`gallery/probe/rows.rs`:图标按钮左缘 4px 带里 Outlined 有环墨、Plain 必须 0 px;
变异后重出图再跑会红)。环在软渲染里是 Outline 叠画布的抗锯齿混色(实测 110/124/110,
本体 123/138/123),所以判据是「这一带有墨/没墨」而不是颜色匹配。

P1-2:ELEVATED CARD 内距 24 + 值 16px 让「2026-10-29 19:22 UTC」折行,同排卡底不齐;
旧测试还断言 20/14(不变量 118+2×padding ≤ 160 变成 166 > 160,假绿)。
按塔裁决「收内距/字号、不破卡片宽度契约」:`PAD` 24 → 20(= card_padding)、值字级 → BodySmall 12。
量测:时戳 14px 墨水宽 138px(160 外框内宽 120 放不下)、12px 为 118px(放得下,余 2px)。
测试改成四条绑一起(字级 12 / 内距 20 / 160−2×PAD ≥ 118 / 2 列实际宽度也 ≥ 118),
并反证「14px 确实放不下」防登记被当噪音删掉;变异回 14px 时红。

P2(顺带):
- 截图通道的主题档由 `--screenshot-theme` 钉死(此前读机器 launcher.json,设置页主题行会漂)
- `gallery::probe` 只认 gallery sheet(必须给 `ULL_GALLERY_SECTION`,否则整页图的 chip 会被当按钮量)、
  行表 8 → 9 行(四式图标按钮)、分隔线判据只在渲染了 divider 段时执行、逐行阈值 40 → 30 并加
  「满档色像素必须少于 38% 像素」的反向判据、probe 拆出 `probe/rows.rs` 守 400 行
- FAB extended 的重复分支合成一处,右内距提取 `EXTENDED_PAD_END`;iced 0.14 无 `Length::Min`,
  「最小宽随标签增长」退化为固定宽已登记
- 五处注释谎言改真(theme/tests、general/theme、elevated、launch_args、app/tests/theme)

`buttons/tests.rs` 加用例后 402 行 ⇒ 图标按钮那组拆到 `buttons/tests/icon.rs`。
2026-11-11 01:49:15 +08:00

87 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 主题三档(Ink / Silver / 跟随系统)的落点测试(M229;从 `app/tests.rs` 拆出守 400 行)。
//!
//! 这一份只管**用户点设置页那三枚 chip 之后**发生的事:换色板、同步设置组状态、落盘。
//! `Auto`(跟随系统)那条路在 `app::tests::the_system_theme_never_overrides_an_explicit_one`。
use crate::app::message::Message;
use crate::app::state::App;
use crate::cli::Options;
use crate::pages;
use crate::theme::Families;
use unlucky_design::color::SchemeFlavor;
use unlucky_presenter::mock::MockBackend;
use unlucky_presenter::prefs::ThemeMode;
use unlucky_presenter::settings_bridge::SettingsBridge;
const FAMILIES: Families = Families {
text: "Segoe UI",
display: "Segoe UI",
mono: "Consolas",
};
/// 一个把配置写在临时文件里的 App:测「落盘」必须真的过文件(内存桥读回来永远是出厂值)。
fn with_config() -> (App, std::path::PathBuf) {
let dir = std::env::temp_dir().join(format!("ull-theme-m229-{}", std::process::id()));
std::fs::create_dir_all(dir.join("config")).expect("临时目录");
let path = dir.join("config/launcher.json");
std::fs::write(&path, "{}").expect("写配置文件");
let app = App::with_presenter(
Options::default(),
FAMILIES,
SchemeFlavor::Light,
Box::new(MockBackend::new()),
SettingsBridge::with_path(&path),
);
(app, dir)
}
/// M229:设置页「通用 → 主题」三档(Ink / Silver / 跟随系统)真的换色板、真的落盘。
///
/// 抓得住:①`pages::Message::Theme` 没接进 `on_page`(点了没反应)②接了但不换 `Ui`
/// (只有选中态变了,界面颜色还是旧的)③同步设置组状态漏了(设置页首帧/刷新后画错档)
/// ④落盘漏了(重启回旧值——这里**真的过文件**:`with_config` 用 `SettingsBridge::with_path` 写临时
/// 配置,再用同一路径重新 `load()` 读回;内存桥读回来永远是出厂值,验不了落盘)。
/// 抓不住:色板好不好看、像素级差异(那要截图,见 `DevDocs/artifacts/m229/`)。
#[test]
fn the_theme_message_switches_the_board_and_persists_it() {
let (mut app, dir) = with_config();
assert_eq!(app.board_name(), "silver", "首帧是浅色主方案(Silver)");
// 选 Ink(= 深色主方案)
let _ = app.update(Message::Page(pages::Message::Theme(ThemeMode::Dark)));
assert_eq!(app.theme_mode(), ThemeMode::Dark);
assert_eq!(app.board_name(), "ink", "Ink 深板要真切过去");
assert_eq!(app.ui().flavor(), SchemeFlavor::Dark);
assert_eq!(
app.settings.language_appearance.theme,
ThemeMode::Dark,
"设置页那一行的档位要跟着改"
);
// 落盘:真的过了文件(重新打开同一个配置读回来就是 dark)
assert_eq!(
SettingsBridge::with_path(dir.join("config/launcher.json"))
.load()
.1,
ThemeMode::Dark,
"主题档要落盘"
);
// 选 Silver(= 浅色主方案)
let _ = app.update(Message::Page(pages::Message::Theme(ThemeMode::Light)));
assert_eq!(app.board_name(), "silver");
assert_eq!(app.ui().flavor(), SchemeFlavor::Light);
assert_eq!(
SettingsBridge::with_path(dir.join("config/launcher.json"))
.load()
.1,
ThemeMode::Light
);
// 同一档再点一次:不许白重建
let refreshes = app.refreshes;
let _ = app.update(Message::Page(pages::Message::Theme(ThemeMode::Light)));
assert_eq!(app.refreshes, refreshes, "同一档不该再重建视图模型");
std::fs::remove_dir_all(&dir).ok();
}