mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
优化窗口框架 (#773)
* fix: 修复 Windows10 中最大化和还原窗口时闪烁的问题 * fix: 修复 Win11 中窗口化状态下标题栏上部被边框遮挡的问题
This commit is contained in:
parent
cd22e966b6
commit
0a9cabdad3
1 changed files with 35 additions and 35 deletions
|
|
@ -140,9 +140,28 @@ protected:
|
|||
{
|
||||
_currentDpi = GetDpiForWindow(_hWnd);
|
||||
|
||||
_UpdateFrameMargins();
|
||||
|
||||
if (!Win32Utils::GetOSVersion().IsWin11()) {
|
||||
// 在 Win10 中,移除标题栏时上边框也被没了。我们的解决方案是:使用 DwmExtendFrameIntoClientArea
|
||||
// 将边框扩展到客户区,然后在顶部绘制了一个黑色实线来显示系统原始边框(这种情况下操作系统将黑色视
|
||||
// 为透明)。因此我们有**完美**的上边框!
|
||||
// 见 https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame
|
||||
//
|
||||
// 有的软件自己绘制了假的上边框,如 Chromium 系、WinUI 3 等,但窗口失去焦点时边框是半透明的,无法
|
||||
// 完美模拟。
|
||||
//
|
||||
// 我们选择扩展到标题栏高度,这是最好的选择。一个自然的想法是,既然上边框只有一个像素高,我们扩展一
|
||||
// 个像素即可,可惜因为 DWM 的 bug,这会使窗口失去焦点时上边框变为透明。那么能否传一个负值,让边框
|
||||
// 扩展到整个客户区?这大部分情况下可以工作,有一个小 bug:不显示边框颜色的设置下深色模式的边框会变
|
||||
// 为纯黑而不是半透明。
|
||||
//
|
||||
// 最大化时不要调用 DwmExtendFrameIntoClientArea 还原边框,会导致窗口闪烁。
|
||||
RECT frame{};
|
||||
AdjustWindowRectExForDpi(&frame, GetWindowStyle(_hWnd), FALSE, 0, _currentDpi);
|
||||
MARGINS margins{
|
||||
.cyTopHeight = -frame.top
|
||||
};
|
||||
DwmExtendFrameIntoClientArea(_hWnd, &margins);
|
||||
|
||||
// 初始化双缓冲绘图
|
||||
static const int _ = []() {
|
||||
BufferedPaintInit();
|
||||
|
|
@ -259,7 +278,7 @@ protected:
|
|||
|
||||
const int topBorderHeight = (int)_GetTopBorderHeight();
|
||||
|
||||
// 在顶部绘制黑色实线以显示系统原始边框,见 _UpdateFrameMargins
|
||||
// 在顶部绘制黑色实线以显示系统原始边框,见 WM_CREATE
|
||||
if (ps.rcPaint.top < topBorderHeight) {
|
||||
RECT rcTopBorder = ps.rcPaint;
|
||||
rcTopBorder.bottom = topBorderHeight;
|
||||
|
|
@ -410,8 +429,6 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
_UpdateFrameMargins();
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
|
|
@ -441,10 +458,17 @@ protected:
|
|||
}
|
||||
|
||||
uint32_t _GetTopBorderHeight() const noexcept {
|
||||
static constexpr uint32_t TOP_BORDER_HEIGHT = 1;
|
||||
// 最大化时没有上边框
|
||||
if (_isMaximized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Win11 或最大化时没有上边框
|
||||
return (Win32Utils::GetOSVersion().IsWin11() || _isMaximized) ? 0 : TOP_BORDER_HEIGHT;
|
||||
// Win10 中窗口边框始终只有一个像素宽,Win11 中的窗口边框宽度和 DPI 缩放有关
|
||||
if (Win32Utils::GetOSVersion().IsWin11()) {
|
||||
return (_currentDpi + USER_DEFAULT_SCREEN_DPI / 2) / USER_DEFAULT_SCREEN_DPI;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int _GetResizeHandleHeight() noexcept {
|
||||
|
|
@ -478,7 +502,9 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
int topBorderHeight = _GetTopBorderHeight();
|
||||
// Win10 中上边框被涂黑来显示系统原始边框,Win11 中 DWM 绘制的上边框也位于客户区内,
|
||||
// 很可能是为了和 Win10 兼容。XAML Islands 不应该和上边框重叠。
|
||||
const int topBorderHeight = (int)_GetTopBorderHeight();
|
||||
|
||||
// SWP_NOZORDER 确保 XAML Islands 窗口始终在标题栏窗口下方,否则主窗口在调整大小时会闪烁
|
||||
SetWindowPos(
|
||||
|
|
@ -499,32 +525,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void _UpdateFrameMargins() const noexcept {
|
||||
if (Win32Utils::GetOSVersion().IsWin11()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MARGINS margins{};
|
||||
if (_GetTopBorderHeight() > 0) {
|
||||
// 在 Win10 中,移除标题栏时上边框也被没了。我们的解决方案是:使用 DwmExtendFrameIntoClientArea
|
||||
// 将边框扩展到客户区,然后在顶部绘制了一个黑色实线来显示系统原始边框(这种情况下操作系统将黑色视
|
||||
// 为透明)。因此我们有**完美**的上边框!
|
||||
// 见 https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame
|
||||
//
|
||||
// 有的软件自己绘制了假的上边框,如 Chromium 系、WinUI 3 等,但窗口失去焦点时边框是半透明的,无法
|
||||
// 完美模拟。
|
||||
//
|
||||
// 我们选择扩展到标题栏高度,这是最好的选择。一个自然的想法是,既然上边框只有一个像素高,我们扩展一
|
||||
// 个像素即可,可惜因为 DWM 的 bug,这会使窗口失去焦点时上边框变为透明。那么能否传一个负值,让边框
|
||||
// 扩展到整个客户区?这大部分情况下可以工作,有一个小 bug:不显示边框颜色的设置下深色模式的边框会变
|
||||
// 为纯黑而不是半透明。
|
||||
RECT frame{};
|
||||
AdjustWindowRectExForDpi(&frame, GetWindowStyle(_hWnd), FALSE, 0, _currentDpi);
|
||||
margins.cyTopHeight = -frame.top;
|
||||
}
|
||||
DwmExtendFrameIntoClientArea(_hWnd, &margins);
|
||||
}
|
||||
|
||||
winrt::event<winrt::delegate<>> _destroyedEvent;
|
||||
|
||||
HWND _hwndXamlIsland = NULL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue