From 4a41346a6428571ec57422ea86e426f252c8ce39 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 18 Jun 2022 12:05:52 +0400 Subject: [PATCH] Use native window resize on Windows 11 --- ui/platform/ui_platform_window_title.cpp | 21 +++-- ui/platform/win/ui_window_shadow_win.cpp | 5 +- ui/platform/win/ui_window_shadow_win.h | 1 - ui/platform/win/ui_window_title_win.cpp | 12 ++- ui/platform/win/ui_window_title_win.h | 4 +- ui/platform/win/ui_window_win.cpp | 109 +++++++++++++++++------ 6 files changed, 108 insertions(+), 44 deletions(-) diff --git a/ui/platform/ui_platform_window_title.cpp b/ui/platform/ui_platform_window_title.cpp index ddb69a8..04bfac2 100644 --- a/ui/platform/ui_platform_window_title.cpp +++ b/ui/platform/ui_platform_window_title.cpp @@ -280,18 +280,21 @@ void TitleControls::raise() { } HitTestResult TitleControls::hitTest(QPoint point, int padding) const { - const auto test = [&]( - const object_ptr &button, - bool close) { + const auto test = [&](const object_ptr &button) { return button && button->geometry().marginsAdded( - { close ? padding : 0, padding, close ? padding : 0, 0 } + { 0, padding, 0, 0 } ).contains(point); }; - if (test(_minimize, false)) { + if (::Platform::IsWindows11OrGreater() + && !_maximizedState + && (point.y() < style::ConvertScale( + window()->windowHandle()->devicePixelRatio()))) { + return HitTestResult::Top; + } else if (test(_minimize)) { return HitTestResult::Minimize; - } else if (test(_maximizeRestore, false)) { + } else if (test(_maximizeRestore)) { return HitTestResult::MaximizeRestore; - } else if (test(_close, true)) { + } else if (test(_close)) { return HitTestResult::Close; } return HitTestResult::None; @@ -586,9 +589,9 @@ std::unique_ptr SetupSeparateTitleControls( controlsTop ? std::move(controlsTop) : rpl::single(0) ) | rpl::start_with_next([=](int width, int padding, int top) { raw->wrap.setGeometry( - padding, + 0, top, - width - 2 * padding, + width, raw->controls.geometry().height()); }, lifetime); diff --git a/ui/platform/win/ui_window_shadow_win.cpp b/ui/platform/win/ui_window_shadow_win.cpp index 79abd98..59bbe55 100644 --- a/ui/platform/win/ui_window_shadow_win.cpp +++ b/ui/platform/win/ui_window_shadow_win.cpp @@ -43,8 +43,7 @@ base::flat_map> ShadowByHandle; WindowShadow::WindowShadow(not_null window, QColor color) : _window(window) -, _handle(GetWindowHandle(window)) -, _windows11(::Platform::IsWindows11OrGreater()) { +, _handle(GetWindowHandle(window)) { init(color); } @@ -317,7 +316,7 @@ void WindowShadow::horCorners(int w, Gdiplus::Graphics *pgraphics0, Gdiplus::Gra } Gdiplus::Color WindowShadow::getColor(uchar alpha) const { - return Gdiplus::Color(BYTE(_windows11 ? 1 : alpha), _r, _g, _b); + return Gdiplus::Color(BYTE(alpha), _r, _g, _b); } Gdiplus::SolidBrush WindowShadow::getBrush(uchar alpha) const { diff --git a/ui/platform/win/ui_window_shadow_win.h b/ui/platform/win/ui_window_shadow_win.h index db63914..c157fa5 100644 --- a/ui/platform/win/ui_window_shadow_win.h +++ b/ui/platform/win/ui_window_shadow_win.h @@ -78,7 +78,6 @@ private: const not_null _window; const HWND _handle; - const bool _windows11 = false; int _x = 0; int _y = 0; diff --git a/ui/platform/win/ui_window_title_win.cpp b/ui/platform/win/ui_window_title_win.cpp index cb3d645..3433df0 100644 --- a/ui/platform/win/ui_window_title_win.cpp +++ b/ui/platform/win/ui_window_title_win.cpp @@ -88,7 +88,7 @@ void TitleWidget::initInWindow(not_null window) { ) | rpl::filter([=](not_null request) { return !isHidden() && geometry().contains(request->point); }) | rpl::start_with_next([=](not_null request) { - request->result = hitTest(request->point); + request->result = hitTest(request->point, request->result); }, lifetime()); SetupSemiNativeSystemButtons(&_controls, window, lifetime(), [=] { @@ -112,9 +112,9 @@ void TitleWidget::refreshGeometryWithWidth(int width) { setGeometry(0, 0, width, _controls.st()->height + add); if (_paddingHelper) { _paddingHelper->controlsParent.setGeometry( + 0, add, - add, - width - 2 * add, + width, _controls.st()->height); } update(); @@ -140,7 +140,9 @@ void TitleWidget::resizeEvent(QResizeEvent *e) { _shadow->setGeometry(0, height() - thickness, width(), thickness); } -HitTestResult TitleWidget::hitTest(QPoint point) const { +HitTestResult TitleWidget::hitTest( + QPoint point, + HitTestResult oldResult) const { const auto origin = _paddingHelper ? _paddingHelper->controlsParent.pos() : QPoint(); @@ -150,6 +152,8 @@ HitTestResult TitleWidget::hitTest(QPoint point) const { const auto controlsResult = _controls.hitTest(point - origin, padding); return (controlsResult != HitTestResult::None) ? controlsResult + : (oldResult != HitTestResult::Client) + ? oldResult : HitTestResult::Caption; } diff --git a/ui/platform/win/ui_window_title_win.h b/ui/platform/win/ui_window_title_win.h index 39177e6..7cce4dc 100644 --- a/ui/platform/win/ui_window_title_win.h +++ b/ui/platform/win/ui_window_title_win.h @@ -55,7 +55,9 @@ protected: private: struct PaddingHelper; - [[nodiscard]] HitTestResult hitTest(QPoint point) const; + [[nodiscard]] HitTestResult hitTest( + QPoint point, + HitTestResult oldResult) const; [[nodiscard]] bool additionalPaddingRequired() const; void refreshGeometryWithWidth(int width); void setAdditionalPadding(int padding); diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index aa71452..9339dd7 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -48,6 +48,13 @@ int(__stdcall *GetSystemMetricsForDpi)( _In_ int nIndex, _In_ UINT dpi); +BOOL(__stdcall *AdjustWindowRectExForDpi)( + _Inout_ LPRECT lpRect, + _In_ DWORD dwStyle, + _In_ BOOL bMenu, + _In_ DWORD dwExStyle, + _In_ UINT dpi); + [[nodiscard]] bool GetDpiForWindowSupported() { static const auto Result = [&] { #define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name) @@ -68,6 +75,16 @@ int(__stdcall *GetSystemMetricsForDpi)( return Result; } +[[nodiscard]] bool AdjustWindowRectExForDpiSupported() { + static const auto Result = [&] { +#define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name) + const auto user32 = base::Platform::SafeLoadLibrary(L"User32.dll"); + return LOAD_SYMBOL(user32, AdjustWindowRectExForDpi); +#undef LOAD_SYMBOL + }(); + return Result; +} + [[nodiscard]] bool IsCompositionEnabled() { auto result = BOOL(FALSE); const auto success = (DwmIsCompositionEnabled(&result) == S_OK); @@ -217,7 +234,6 @@ WindowHelper::WindowHelper(not_null window) , _handle(ResolveWindowHandle(window)) , _title(Ui::CreateChild(window.get())) , _body(Ui::CreateChild(window.get())) -, _shadow(std::in_place, window, st::windowShadowFg->c) , _dpi(GetDpiForWindowSupported() ? GetDpiForWindow(_handle) : 0) { Expects(_handle != nullptr); @@ -273,13 +289,14 @@ void WindowHelper::setNativeFrame(bool enabled) { } } _title->setVisible(!enabled); - if (enabled) { + if (enabled || ::Platform::IsWindows11OrGreater()) { _shadow.reset(); } else { _shadow.emplace(window(), st::windowShadowFg->c); _shadow->setResizeEnabled(!fixedSize()); initialShadowUpdate(); } + updateCornersRounding(); updateMargins(); updateWindowFrameColors(); fixMaximizedWindow(); @@ -298,6 +315,9 @@ void WindowHelper::setNativeFrame(bool enabled) { } void WindowHelper::initialShadowUpdate() { + if (!_shadow) { + return; + } using Change = WindowShadow::Change; const auto noShadowStates = (Qt::WindowMinimized | Qt::WindowMaximized); if ((window()->windowState() & noShadowStates) || window()->isHidden()) { @@ -305,7 +325,6 @@ void WindowHelper::initialShadowUpdate() { } else { _shadow->update(Change::Moved | Change::Resized | Change::Shown); } - updateCornersRounding(); } void WindowHelper::updateCornersRounding() { @@ -397,12 +416,41 @@ void WindowHelper::init() { size.height() - (titleShown ? titleHeight : 0)); }, _body->lifetime()); + hitTestRequests( + ) | rpl::filter([=](not_null request) { + return ::Platform::IsWindows11OrGreater(); + }) | rpl::start_with_next([=](not_null request) { + request->result = [=] { + RECT r{}; + const auto style = GetWindowLongPtr(_handle, GWL_STYLE) + & ~WS_CAPTION; + const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE); + const auto dpi = style::ConvertScale( + 96 * style::DevicePixelRatio()); + if (AdjustWindowRectExForDpiSupported() && dpi) { + AdjustWindowRectExForDpi(&r, style, false, styleEx, dpi); + } else { + AdjustWindowRectEx(&r, style, false, styleEx); + } + const auto maximized = window()->isMaximized() + || window()->isFullScreen(); + return (!maximized && (request->point.y() < -r.top)) + ? HitTestResult::Top + : HitTestResult::Client; + }(); + }, window()->lifetime()); + updateMargins(); + if (!::Platform::IsWindows11OrGreater()) { + _shadow.emplace(window(), st::windowShadowFg->c); + } + if (!::Platform::IsWindows8OrGreater()) { SetWindowTheme(_handle, L" ", L" "); QApplication::setStyle(QStyleFactory::create("Windows")); } + updateWindowFrameColors(); const auto handleStateChanged = [=](Qt::WindowState state) { @@ -427,6 +475,7 @@ void WindowHelper::init() { handleStateChanged); initialShadowUpdate(); + updateCornersRounding(); auto dm = std::make_unique(window()); if (dm->valid()) { @@ -506,11 +555,16 @@ bool WindowHelper::handleNativeEvent( if (_title->isHidden() || window()->isFullScreen() || !wParam) { return false; } - WINDOWPLACEMENT wp; - wp.length = sizeof(WINDOWPLACEMENT); - if (GetWindowPlacement(_handle, &wp) - && (wp.showCmd == SW_SHOWMAXIMIZED)) { - const auto r = &((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0]; + const auto r = &((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0]; + const auto maximized = [&] { + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + return GetWindowPlacement(_handle, &wp) + && (wp.showCmd == SW_SHOWMAXIMIZED); + }(); + const auto addBorders = maximized + || ::Platform::IsWindows11OrGreater(); + if (addBorders) { const auto dpi = _dpi.current(); const auto borderWidth = (GetSystemMetricsForDpiSupported() && dpi) ? GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) @@ -519,8 +573,12 @@ bool WindowHelper::handleNativeEvent( + GetSystemMetrics(SM_CXPADDEDBORDER); r->left += borderWidth; r->right -= borderWidth; - r->top += borderWidth; + if (maximized) { + r->top += borderWidth; + } r->bottom -= borderWidth; + } + if (maximized) { const auto hMonitor = MonitorFromWindow( _handle, MONITOR_DEFAULTTONEAREST); @@ -536,10 +594,8 @@ bool WindowHelper::handleNativeEvent( case ABE_BOTTOM: r->bottom -= 1; break; } } - if (result) *result = 0; - } else { - if (result) *result = WVR_REDRAW; } + if (result) *result = addBorders ? 0 : WVR_REDRAW; } return true; case WM_NCRBUTTONUP: { @@ -569,15 +625,15 @@ bool WindowHelper::handleNativeEvent( case WM_WINDOWPOSCHANGING: case WM_WINDOWPOSCHANGED: { + auto placement = WINDOWPLACEMENT{ + .length = sizeof(WINDOWPLACEMENT), + }; + if (!GetWindowPlacement(_handle, &placement)) { + LOG(("System Error: GetWindowPlacement failed.")); + return false; + } + _title->refreshAdditionalPaddings(_handle, placement); if (_shadow) { - auto placement = WINDOWPLACEMENT{ - .length = sizeof(WINDOWPLACEMENT), - }; - if (!GetWindowPlacement(_handle, &placement)) { - LOG(("System Error: GetWindowPlacement failed.")); - return false; - } - _title->refreshAdditionalPaddings(_handle, placement); if (placement.showCmd == SW_SHOWMAXIMIZED || placement.showCmd == SW_SHOWMINIMIZED) { _shadow->update(WindowShadow::Change::Hidden); @@ -606,8 +662,8 @@ bool WindowHelper::handleNativeEvent( window()->windowHandle()->windowStateChanged(state); } updateMargins(); + _title->refreshAdditionalPaddings(_handle); if (_shadow) { - _title->refreshAdditionalPaddings(_handle); const auto changes = (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXIMIZED) ? WindowShadow::Change::Hidden @@ -630,8 +686,8 @@ bool WindowHelper::handleNativeEvent( } return false; case WM_MOVE: { + _title->refreshAdditionalPaddings(_handle); if (_shadow) { - _title->refreshAdditionalPaddings(_handle); _shadow->update(WindowShadow::Change::Moved); } } return false; @@ -647,9 +703,9 @@ bool WindowHelper::handleNativeEvent( const auto mapped = QPoint( int(std::floor(p.x / ratio)), int(std::floor(p.y / ratio))); - *result = [&] { + *result = [&]() -> LRESULT { if (!window()->rect().contains(mapped)) { - return HTTRANSPARENT; + return DefWindowProc(_handle, msg, wParam, lParam); } auto request = HitTestRequest{ .point = mapped, @@ -672,7 +728,7 @@ bool WindowHelper::handleNativeEvent( case HitTestResult::Close: return systemButtonHitTest(result); case HitTestResult::None: - default: return HTTRANSPARENT; + default: return DefWindowProc(_handle, msg, wParam, lParam); }; }(); _systemButtonOver.fire(systemButtonHitTest(*result)); @@ -814,7 +870,7 @@ void WindowHelper::updateWindowFrameColors(bool active) { } void WindowHelper::updateMargins() { - if (_updatingMargins) return; + if (::Platform::IsWindows11OrGreater() || _updatingMargins) return; _updatingMargins = true; const auto guard = gsl::finally([&] { _updatingMargins = false; }); @@ -888,6 +944,7 @@ void WindowHelper::updateMargins() { } void WindowHelper::fixMaximizedWindow() { + if (::Platform::IsWindows11OrGreater()) return; auto r = RECT(); GetClientRect(_handle, &r); const auto style = GetWindowLongPtr(_handle, GWL_STYLE);