Files
Unlucky-Launcher/crates/app-iced/src/app/update/appearance.rs
T
AskaEth 8a8e5ca0c0
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
CI / runtime smoke (ubuntu x64, xvfb) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Unlucky Launcher v0.2.0
首个 iced + Material 3 Expressive 版本的源码快照。
完整历史与日常提交在 dev 分支;main 只放阶段性快照。
2026-11-05 14:22:11 +08:00

82 lines
4.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.
//! M116:窗口圆角的下发落点(`impl App::apply_window_corner`)。
//!
//! ## 为什么单独成文件
//!
//! 圆角与整窗 α 是**两条独立的管线**,只是都必须拿到 HWND、都在同一个时点下发:
//!
//! | 管线 | 判据 | 落点 | 键 |
//! |---|---|---|---|
//! | 整窗 α(D36) | `platform::effective_window_alpha` | `SetLayeredWindowAttributes` | `appearance.window_opacity` |
//! | 圆角(M116) | `platform::plan_corner` | `DwmSetWindowAttribute` | `appearance.window_corner` |
//!
//! 合在一个函数里会让「α 变了要不要重设圆角」这种问题变成实现细节;分开之后两个判据各自可测。
//!
//! ## 日志为什么是**一行、六栏**
//!
//! `corner=policy=… source=dwm|square os=… excluded_reason=… radius=system pref=… readback=… frame=… layered=…`
//!
//! 用户报的现象是「别的窗口都有圆角,只有我们的没有」。没有这一行,事后无法区分
//! 「没生效」「生效了但看不见」「这台系统根本没有这个能力」;`frame` / `layered` 两栏则是
//! 「圆角为什么不生效」的两个现场读数:`frame=`(窗口形状)与 `layered=`(整窗 α 引入的分层位)。
//!
//! ⚠️ **产品窗口不是 `WS_POPUP`**(M116 r4 复审订正):winit 在 `decorations = false` 下给的是
//! `WS_CAPTION|WS_BORDER|WS_CLIPSIBLINGS|WS_SYSMENU`(可缩放时再加 `WS_THICKFRAME` 与 min/max 键),
//! `WS_POPUP` 只在**带 owner** 的弹出窗才加;无边框观感来自 `WM_NCCALCSIZE` 把客户区铺满。
//! 所以真进程日志里 `frame=` 应是 `overlapped`(关掉 min/max 则 `other`),**永远不会是 `popup`**。
//! 真正让系统忽略圆角请求的是**整窗 α 补的 `WS_EX_LAYERED`**(`layered=on`)。
//!
//! `radius=system` 是刻意的:系统不暴露 DWM 的圆角半径,我们也不自己定一个数字
//! (M116 取消了 8/12/16 固定档)。
use crate::platform;
use crate::platform::win32::corner::{self, CornerApplied};
use crate::platform::win32::transparency::{apply_corner, style_summary, CornerOutcome};
use super::super::state::App;
impl App {
/// 把当前圆角策略下发到窗口,并打一行可分辨的日志。
///
/// ## 去重(M116 r1 复审 P2-4)
///
/// 设置页的滑条(整窗 α / 面板 α / 暗化)每动一下都会走一次外观变更,而<b>圆角与那些键无关</b>。
/// 判定完全没变时这里**直接返回**:不动窗口、不打日志。判据是
/// [`corner::needs_redispatch`](纯函数,有「同一策略 N 次只记 1 行」的测试),
/// 指纹只含「策略 / 来源 / 原因码 / 有没有句柄」,不含只有真下发才拿得到的读数。
///
/// 没有句柄时**也打日志**(`window=none`):不能让「没生效」只体现为屏幕上没变化。
/// 句柄从无到有会让指纹变化 ⇒ 首次拿到 HWND 时必然重下一次。
pub(crate) fn apply_window_corner(&mut self, context: &str) {
let policy = self.appearance.window_corner();
let plan = platform::plan_corner(
policy,
platform::current_windows_version(),
self.appearance.backend,
);
// 整窗 α < 1 ⇒ `window_alpha::dispatch` 会给窗口补 `WS_EX_LAYERED`;系统因此忽略圆角请求
// (tower 裁定 A / D47)。这里只记**事实**:α 是否把我们自己的分层位打开了。
let alpha_layered = self.appearance.window_opacity < 1.0;
let suppressed_by = alpha_layered.then_some("window_alpha");
let next = CornerApplied::new(policy, &plan, self.hwnd.is_some(), alpha_layered);
if !corner::needs_redispatch(self.corner_applied.as_ref(), &next) {
return;
}
self.corner_applied = Some(next);
// 行的形状只有一处(`corner::format_line`),因为那里能在没有窗口的环境里被单测钉住。
// 关键的三栏必须在同一行:`pref=`(请求发出去了)、`layered=`(分层位在不在,来自
// 整窗 α)、`frame=`(窗口形状 = `overlapped`,不是 `popup`)——否则以后只能猜是哪一条。
let (outcome, style) = match self.hwnd {
Some(hwnd) => (apply_corner(hwnd, &plan).describe(), style_summary(hwnd)),
None => (
CornerOutcome::NoWindow.describe(),
"frame=? layered=?".to_owned(),
),
};
eprintln!(
"{}",
corner::format_line(context, &plan, policy, &outcome, &style, suppressed_by)
);
}
}