feat(app-iced): M93 —— 外观接线:整窗 α / 面板 α / 背景图 / 配色来源
把 appearance.* 那批键接到窗口与像素上(M91 只落了键与判据,零调用方)。
整窗 α(D34/D36)
- `ensure_layered(hwnd)` 落在唯一 unsafe 文件 `platform/win32/transparency.rs`
(`WS_EX_LAYERED` 只有 Get/SetWindowLongPtrW 能加,且 CI 的 unsafe-scope 是集合相等);
`app/boot.rs` 只负责开窗,句柄到手后由 `Message::WindowHandle` → `window_alpha::dispatch`
先补样式再下发。本机实测 `layered=ok outcome=Applied { byte: 205 }`(α=0.8)。
- 三条入口都会重算并下发:启动(句柄到达)、设置页改动、配置文件热切。
- `layered` 日志有三态:`n/a`(α≥1,压根不需要)/ `ok` / `FAILED`,避免把「没用到」写成假红。
面板 α 与 CPU 档(D36)
- 面板 α 独立于整窗 α:只作用到外壳层(根容器 / 标题栏 / 导航轨)与外观卡片;
CPU 档强制 1.0、玻璃强制关(`pages/appearance/alpha.rs` 的真值表 + 单测)。
- 色卡档继续走令牌 baseline,只有取色档(①②级)才换 `Ui` 的 scheme(`Ui::from_scheme`)。
背景图(D38)
- 读盘解码(png)→ cover/contain/tile 三种填充几何 → `bg_dim` 向底色退 → 36dp 网格马赛克;
iced 的 `image` widget 需要 `iced/image` feature(Cargo.toml 不在 scope),故为已登记降级。
- 读盘失败静默回落 + 一条可读诊断(设置页错误行 + 日志),配色经 `Provenance` 回落色卡。
设置页「外观」分组
- 整窗透明度滑块(下限 0.08)/ 面板透明度(CPU 档禁用并写明 55%→100% 的降级)/
背景图开关与路径 / 填充三档 / 暗化 / 配色来源三档;改完即时生效并写回:
bridge 覆盖的 5 个键走 `SettingsBridge::persist`,`window_opacity` / `palette_source`
走 `AppCommand::SetSetting`(每个键只有一个写者)。
- `SettingsBridge::load_base()`(presenter)让 app-iced 不需要直接依赖 core 就能读回
`window_opacity` / `palette_source`。
取证
- `ULL_APPEARANCE_*` / `ULL_START_ROUTE` 只影响本次进程(语义同 ULL_TICK_TRACE),
截图通道不读用户配置(D30)。
This commit is contained in:
1 parent
eae7ae0ff4
commit
b8ab46662c
24 files changed
+3119
-48
No files matched your search
@@ -80,6 +80,22 @@ impl Ui {
|
||||
self.scheme.flavor()
|
||||
}
|
||||
|
||||
/// 用**已解析的方案**构造求值器(D38 的落点:背景图 / 桌面壁纸取色 → 49 个角色换掉 baseline)。
|
||||
///
|
||||
/// 只是**追加**的构造入口:既有的 [`Ui::new`] / [`Ui::with_platform`] 一字未动,默认仍是
|
||||
/// [`Scheme::baseline`]。调用方(`app` 侧)拿到 `unlucky_design::palette::resolve_source` 的
|
||||
/// 结果后走这里,于是「配色来源」真的能改颜色,而不只是一条日志。
|
||||
///
|
||||
/// 平台与骨架尺寸仍按当前环境求值——方案只带颜色,不带平台。
|
||||
pub fn from_scheme(scheme: Scheme, families: Families) -> Self {
|
||||
Self {
|
||||
scheme,
|
||||
platform: Platform::current(),
|
||||
metrics: LayoutMetrics::DESKTOP,
|
||||
families,
|
||||
}
|
||||
}
|
||||
|
||||
/// 当前平台(决定字体族与字重降级)。
|
||||
pub fn platform(&self) -> Platform {
|
||||
self.platform
|
||||
@@ -331,4 +347,47 @@ mod tests {
|
||||
assert_eq!(hovered.a, 1.0, "状态层必须给出不透明结果");
|
||||
assert_ne!(hovered, ui.color(Role::Surface));
|
||||
}
|
||||
|
||||
/// D38:配色来源解析出来的方案必须**真的换色**(M93)。
|
||||
///
|
||||
/// 抓得住:①`from_scheme` 用的是传进来的方案,不是 baseline(配一张蓝底的取色方案,
|
||||
/// primary 与 baseline 不同);②默认构造(`new` / `with_platform`)逐字段等于 baseline
|
||||
/// ——即「不传 scheme 时行为不变」,这条是防回流的那一半。
|
||||
/// 抓不住:取色结果好不好看、设置页是否真的切过去(前者是设计判断,后者由 app 侧日志举证)。
|
||||
#[test]
|
||||
fn a_resolved_scheme_replaces_the_baseline_colours() {
|
||||
use unlucky_design::palette::{resolve_source, CardKey, ImageInput};
|
||||
|
||||
// 4×4 的纯红图:种子彩度远高于门槛,必然走到「背景图取色」这一级。
|
||||
let pixels = [0xC0_u8, 0x20, 0x20, 0xFF].repeat(16);
|
||||
let input = ImageInput::pixels(&pixels, 4, 4);
|
||||
let (scheme, provenance) =
|
||||
resolve_source(Some(input), None, Some(CardKey::Sky), SchemeFlavor::Light);
|
||||
assert_eq!(
|
||||
provenance.level,
|
||||
unlucky_design::palette::Level::BackgroundImage,
|
||||
"纯红图必须取色成功(否则本测试的输入不合格)"
|
||||
);
|
||||
|
||||
let tinted = Ui::from_scheme(scheme, FAMILIES);
|
||||
let baseline = ui(SchemeFlavor::Light);
|
||||
assert_ne!(
|
||||
tinted.color(Role::Primary),
|
||||
baseline.color(Role::Primary),
|
||||
"取色方案必须换掉 primary"
|
||||
);
|
||||
// 深浅仍跟着方案走(方案自带 flavor),不会把 Light 变成 Dark。
|
||||
assert_eq!(tinted.flavor(), SchemeFlavor::Light);
|
||||
assert_eq!(tinted.name(), baseline.name());
|
||||
|
||||
// 默认构造不变:`with_platform` 与 baseline 逐块相等。
|
||||
let explicit = Ui::from_scheme(Scheme::baseline(SchemeFlavor::Light), FAMILIES);
|
||||
for role in Role::ALL {
|
||||
assert_eq!(
|
||||
explicit.color(role),
|
||||
baseline.color(role),
|
||||
"{role:?}:不传 scheme 时默认行为必须与改动前一致"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user