fix(app-iced): M93 r3 —— 复审 r1 四条返工(Canvas 不吃 α / 平铺补缝 / 诊断上移 / 文档)
先 `git rebase main`(M92 `eae7ae0` 已在主线;`Cargo.lock` 与 `crates/app-iced/Cargo.toml`
自动合并,`image-without-codecs` 与 M92 的 `unlucky-i18n` 两段都在)。
P2-2(口径违约)Canvas 层不许吃面板 α
- `Ui::layer()` 改成按令牌 `MaterialLayer::allows_translucency()` 判定:Canvas 恒不透明,
只有 Card / Overlay 乘 `panel_alpha`。判据取令牌的不变量,三个调用点(标题栏 / 导航轨 /
页头带)没有各自特判。
- 守门:`the_canvas_layer_stays_opaque_while_panels_take_the_alpha`(α=0.55 时 Canvas.a==1.0、
Card/Overlay.a==0.55;CPU 档连卡片都是 1.0)。
P2-1 平铺不许留缝
- `background::tiles()` 超 `MAX_TILES` 时改成**放大图块步长**(`Tiles { cols, rows, scale }`,
按 1.15 逐级放大到块数进预算),而不是减列/减行;`MAX_TILE_SCALE` 提到 1024 只是安全阀
(1×1 的图在 1180×760 上需要 ~118 倍才能用 64 块盖满)。
- `picture::tiled()` 用 `ContentFit::Fill` + 格子尺寸 = `源尺寸 × scale`(同比例,不变形):
`None` 会按图片原生尺寸画、比格子小就留空,正是那道缝。
- 守门:`tile_grid_always_covers_the_viewport_within_budget`(6 组尺寸,含 120×120 与 1×1,
断言 `covered >= viewport` 且 `count <= MAX_TILES`);背景层底下本来就有不透明画布底色兜底。
P2-3 诊断上移到首屏
- 诊断块(读盘失败红字 / CPU 降级 / 配色回落 / 当前配色层级)从卡片末尾移到「背景图路径」
紧下方:1180×760 首屏可见(截图 `r4-broken-path.png` 里红字在 y≈594)。
P2-4 文档对齐
- `transparency.rs`:`WS_EX_LAYERED` 的归属改成「本文件的 `ensure_layered` 负责」,
窗口创建侧只拿句柄;`platform/mod.rs` / `lib.rs` 的模块表同步。
- 删掉「马赛克取样」这类已删实现的现在时描述(`background` / `picture` / `app` 的注释),
保留一句 r1→r2 的历史说明;`app/tests/appearance.rs` 的测试意图跟着改。
门禁(工作区级,最终树):`cargo fmt --all --check` 干净;
`cargo clippy --workspace --all-targets -- -D warnings` 无输出;
`cargo test --workspace` 全绿(app-iced 299 lib + 6 集成守门、presenter 126、其余 crate 全过)。
出图:`r4-canvas-opaque-panel55{,-dark}.png`(Canvas 不透明 + 面板透底)、
`r4-tile-120.png`(120×120 平铺右缘无裸白)、`r4-broken-path.png`(诊断首屏可见)。
This commit is contained in:
1 parent
22e4c423fa
commit
5c40c64b5f
13 files changed
+181
-85
No files matched your search
@@ -110,7 +110,7 @@ pub struct App {
|
||||
pub(super) refreshes: u64,
|
||||
/// 首屏快照到位后是否已经自动选中过第一行(list-detail 的桌面惯例,见 `update::auto_select`)。
|
||||
pub(super) auto_selected: bool,
|
||||
/// 窗口高度(背景图马赛克的视口判据;宽度在 `width`)。
|
||||
/// 窗口高度(背景图平铺网格的视口判据;宽度在 `width`)。
|
||||
pub(super) height: f32,
|
||||
/// 外观运行期状态(M93):面板 α / 背景图 / 配色来源 / 诊断。
|
||||
pub(super) appearance: pages::appearance::State,
|
||||
|
||||
@@ -144,9 +144,9 @@ fn a_broken_background_path_falls_back_silently_but_says_why() {
|
||||
);
|
||||
}
|
||||
|
||||
/// 抓得住:真 PNG → 背景真的生效(有马赛克、有格数、配色走「背景图取色」这一级)。
|
||||
/// 抓不住:马赛克的观感(36dp 网格是已登记的降级),以及填充三种档的视觉差异(几何由
|
||||
/// `background` 的纯函数测试守)。
|
||||
/// 抓得住:真 PNG → 背景真的生效(解出原图尺寸、路径留给渲染器、配色走「背景图取色」这一级)。
|
||||
/// 抓不住:渲染出来的像素(由截图举证),以及三种填充的覆盖范围(由 `background::tiles` 的
|
||||
/// 「覆盖 ≥ 视口」断言守)。
|
||||
#[test]
|
||||
fn a_decodable_png_makes_the_background_and_the_palette_active() {
|
||||
let dir = std::env::temp_dir().join(format!("ull-appear-png-{}", std::process::id()));
|
||||
@@ -211,6 +211,47 @@ fn the_settings_page_renders_the_real_appearance_group() {
|
||||
let _tree = pages::page_with_appearance(&app.ui, &app.view, &app.appearance);
|
||||
}
|
||||
|
||||
/// **P2-2 的守门(复审 r1)**:面板 α 只作用到允许半透明的层。
|
||||
///
|
||||
/// 抓得住:`Ui::layer(Canvas)` 在 α=0.55 时仍是不透明(标题栏/导航轨/页头带都取 Canvas,
|
||||
/// 它们一旦透底,三个窗口键与导航文字就压在背景照片上);`Card` / `Overlay` 必须真的吃到 α。
|
||||
/// 抓不住:真实合成后的观感(那由截图看)。
|
||||
#[test]
|
||||
fn the_canvas_layer_stays_opaque_while_panels_take_the_alpha() {
|
||||
use unlucky_design::elevation::MaterialLayer;
|
||||
|
||||
let mut app = headless(Options::default(), SchemeFlavor::Light);
|
||||
app.appearance.apply(Change::PanelAlpha(0.55));
|
||||
app.rebuild_ui();
|
||||
|
||||
let ui = app.ui();
|
||||
assert_eq!(ui.panel_alpha(), 0.55, "生效值进了 Ui");
|
||||
assert_eq!(
|
||||
ui.layer(MaterialLayer::Canvas).a,
|
||||
1.0,
|
||||
"窗口底(标题栏/导航轨/页头带)永远不透明——令牌的不变量"
|
||||
);
|
||||
for layer in [MaterialLayer::Card, MaterialLayer::Overlay] {
|
||||
assert!(
|
||||
(ui.layer(layer).a - 0.55).abs() < 1e-6,
|
||||
"{layer:?} 必须吃面板 α(实际 {})",
|
||||
ui.layer(layer).a
|
||||
);
|
||||
}
|
||||
|
||||
// CPU 档:α 被强制 1.0 ⇒ 连卡片也不透明。
|
||||
let mut cpu = headless(
|
||||
Options {
|
||||
backend: Backend::Cpu,
|
||||
..Options::default()
|
||||
},
|
||||
SchemeFlavor::Light,
|
||||
);
|
||||
cpu.appearance.apply(Change::PanelAlpha(0.55));
|
||||
cpu.rebuild_ui();
|
||||
assert_eq!(cpu.ui().layer(MaterialLayer::Card).a, 1.0, "CPU 档强制 1.0");
|
||||
}
|
||||
|
||||
/// 抓得住:**把滑块从头拖到尾**(0.00 → 1.00 的 11 个档)时,状态里的整窗 α 与下发字节逐档单调
|
||||
/// 上升、且最低一档就是下限字节 21(= 0.0824 ≥ 0.08)、最后一档不透明(不给字节)。
|
||||
/// 这就是「拖动 → 实际 α」的链条在数值上的等价物;真机拖动的那一次由二进制日志举证。
|
||||
|
||||
@@ -87,7 +87,7 @@ impl App {
|
||||
Message::Window(event) => {
|
||||
if let window::Event::Resized(size) = event {
|
||||
self.width = size.width;
|
||||
// 高度是背景马赛克的视口判据之一(宽度早就有),以前只记宽是因为没有背景层。
|
||||
// 高度是背景平铺网格的视口判据之一(宽度早就有),以前只记宽是因为没有背景层。
|
||||
self.height = size.height;
|
||||
self.view_dirty = true;
|
||||
self.refresh_view();
|
||||
|
||||
Reference in new issue
Block a user