diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index fc3fb18..89c2793 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -20,10 +20,12 @@ #include #include #include +#include #include #include -#include + +Q_DECLARE_METATYPE(QMargins); namespace Ui { namespace Platform { @@ -37,10 +39,6 @@ constexpr auto kDWMWA_TEXT_COLOR = DWORD(36); UINT(__stdcall *GetDpiForWindow)(_In_ HWND hwnd); -int(__stdcall *GetSystemMetricsForDpi)( - _In_ int nIndex, - _In_ UINT dpi); - [[nodiscard]] bool GetDpiForWindowSupported() { static const auto Result = [&] { #define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name) @@ -51,16 +49,6 @@ int(__stdcall *GetSystemMetricsForDpi)( return Result; } -[[nodiscard]] bool GetSystemMetricsForDpiSupported() { - 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, GetSystemMetricsForDpi); -#undef LOAD_SYMBOL - }(); - return Result; -} - [[nodiscard]] bool IsCompositionEnabled() { auto result = BOOL(FALSE); const auto success = (DwmIsCompositionEnabled(&result) == S_OK); @@ -119,11 +107,29 @@ int(__stdcall *GetSystemMetricsForDpi)( return bAutoHidden; } +void FixAeroSnap(HWND handle) { + SetWindowLongPtr( + handle, + GWL_STYLE, + GetWindowLongPtr(handle, GWL_STYLE) | WS_CAPTION | WS_THICKFRAME); +} + +[[nodiscard]] HWND ResolveWindowHandle(not_null widget) { + if (!::Platform::IsWindows8OrGreater()) { + widget->setWindowFlag(Qt::FramelessWindowHint); + } + const auto result = GetWindowHandle(widget); + if (!::Platform::IsWindows8OrGreater()) { + FixAeroSnap(result); + } + return result; +} + } // namespace WindowHelper::WindowHelper(not_null window) : BasicWindowHelper(window) -, _handle(GetWindowHandle(window)) +, _handle(ResolveWindowHandle(window)) , _title(Ui::CreateChild(window.get())) , _body(Ui::CreateChild(window.get())) , _shadow(std::in_place, window, st::windowShadowFg->c) @@ -175,11 +181,10 @@ void WindowHelper::setTitleStyle(const style::WindowTitle &st) { void WindowHelper::setNativeFrame(bool enabled) { if (!::Platform::IsWindows8OrGreater()) { - const auto style = GetWindowLongPtr(_handle, GWL_STYLE); - SetWindowLongPtr( - _handle, - GWL_STYLE, - enabled ? (style | WS_CAPTION) : (style & ~WS_CAPTION)); + window()->windowHandle()->setFlag(Qt::FramelessWindowHint, !enabled); + if (!enabled) { + FixAeroSnap(_handle); + } } _title->setVisible(!enabled); if (enabled) { @@ -189,7 +194,9 @@ void WindowHelper::setNativeFrame(bool enabled) { _shadow->setResizeEnabled(!fixedSize()); initialShadowUpdate(); } + updateMargins(); updateWindowFrameColors(); + fixMaximizedWindow(); SetWindowPos( _handle, 0, @@ -246,6 +253,7 @@ void WindowHelper::setGeometry(QRect rect) { void WindowHelper::showFullScreen() { if (!_isFullScreen) { _isFullScreen = true; + updateMargins(); updateCornersRounding(); } window()->showFullScreen(); @@ -255,6 +263,7 @@ void WindowHelper::showNormal() { window()->showNormal(); if (_isFullScreen) { _isFullScreen = false; + updateMargins(); updateCornersRounding(); } } @@ -299,6 +308,8 @@ void WindowHelper::init() { size.height() - (titleShown ? titleHeight : 0)); }, _body->lifetime()); + updateMargins(); + if (!::Platform::IsWindows8OrGreater()) { SetWindowTheme(_handle, L" ", L" "); QApplication::setStyle(QStyleFactory::create("Windows")); @@ -372,7 +383,7 @@ bool WindowHelper::handleNativeEvent( } return true; case WM_NCCALCSIZE: { - if (_title->isHidden() || window()->isFullScreen() || !wParam) { + if (_title->isHidden() || !wParam) { return false; } WINDOWPLACEMENT wp; @@ -380,38 +391,31 @@ bool WindowHelper::handleNativeEvent( if (GetWindowPlacement(_handle, &wp) && (wp.showCmd == SW_SHOWMAXIMIZED)) { const auto r = &((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0]; - const auto dpi = _dpi.current(); - const auto style = GetWindowLongPtr(_handle, GWL_STYLE); - const auto borderWidth = ((GetSystemMetricsForDpiSupported() && dpi) - ? GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) - + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) - : GetSystemMetrics(SM_CXSIZEFRAME) - + GetSystemMetrics(SM_CXPADDEDBORDER)) - - ((style & WS_CAPTION) ? 0 : 1); - r->left += borderWidth; - r->right -= borderWidth; - r->top += borderWidth; - r->bottom -= borderWidth; - const auto hMonitor = MonitorFromWindow( - _handle, + const auto hMonitor = MonitorFromPoint( + { (r->left + r->right) / 2, (r->top + r->bottom) / 2 }, MONITOR_DEFAULTTONEAREST); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - UINT uEdge = (UINT)-1; - if (GetMonitorInfo(hMonitor, &mi) - && IsTaskbarAutoHidden(&mi.rcMonitor, &uEdge)) { - switch (uEdge) { - case ABE_LEFT: r->left += 1; break; - case ABE_RIGHT: r->right -= 1; break; - case ABE_TOP: r->top += 1; break; - case ABE_BOTTOM: r->bottom -= 1; break; + if (hMonitor) { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(hMonitor, &mi)) { + *r = mi.rcWork; + UINT uEdge = (UINT)-1; + if (IsTaskbarAutoHidden(&mi.rcMonitor, &uEdge)) { + switch (uEdge) { + case ABE_LEFT: r->left += 1; break; + case ABE_RIGHT: r->right -= 1; break; + case ABE_TOP: r->top += 1; break; + case ABE_BOTTOM: r->bottom -= 1; break; + } + } } } if (result) *result = 0; } else { if (result) *result = WVR_REDRAW; } - } return true; + return true; + } case WM_NCRBUTTONUP: { if (_title->isHidden()) { @@ -473,6 +477,7 @@ bool WindowHelper::handleNativeEvent( } window()->windowHandle()->windowStateChanged(state); } + updateMargins(); if (_shadow) { _title->refreshAdditionalPaddings(_handle); const auto changes = (wParam == SIZE_MINIMIZED @@ -486,27 +491,6 @@ bool WindowHelper::handleNativeEvent( } return false; case WM_SHOWWINDOW: { - const auto style = GetWindowLongPtr(_handle, GWL_STYLE); - if (!::Platform::IsWindows8OrGreater() - && !_title->isHidden() - && (style & WS_CAPTION)) { - SetWindowLongPtr( - _handle, - GWL_STYLE, - style & ~WS_CAPTION); - SetWindowPos( - _handle, - 0, - 0, - 0, - 0, - 0, - SWP_FRAMECHANGED - | SWP_NOMOVE - | SWP_NOSIZE - | SWP_NOZORDER - | SWP_NOACTIVATE); - } if (_shadow) { const auto style = GetWindowLongPtr(_handle, GWL_STYLE); const auto changes = WindowShadow::Change::Resized @@ -529,9 +513,12 @@ bool WindowHelper::handleNativeEvent( return false; } - POINT p{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(_handle, &p); - const auto mapped = QPoint(p.x, p.y) / window()->devicePixelRatioF(); + const auto p = MAKEPOINTS(lParam); + auto r = RECT(); + GetWindowRect(_handle, &r); + const auto mapped = QPoint( + p.x - r.left + _marginsDelta.left(), + p.y - r.top + _marginsDelta.top()); *result = [&] { if (!window()->rect().contains(mapped)) { return HTTRANSPARENT; @@ -691,6 +678,99 @@ void WindowHelper::updateWindowFrameColors(bool active) { sizeof(COLORREF)); } +void WindowHelper::updateMargins() { + if (_updatingMargins) return; + + _updatingMargins = true; + const auto guard = gsl::finally([&] { _updatingMargins = false; }); + + RECT r, a; + + GetClientRect(_handle, &r); + a = r; + + const auto style = GetWindowLongPtr(_handle, GWL_STYLE); + const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE); + AdjustWindowRectEx(&a, style, false, styleEx); + auto margins = QMargins( + a.left - r.left, + a.top - r.top, + r.right - a.right, + r.bottom - a.bottom); + if (style & WS_MAXIMIZE) { + RECT w, m; + GetWindowRect(_handle , &w); + m = w; + + HMONITOR hMonitor = MonitorFromRect(&w, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + m = mi.rcWork; + } + + _marginsDelta = QMargins( + w.left - m.left, + w.top - m.top, + m.right - w.right, + m.bottom - w.bottom); + + margins.setLeft(margins.left() - _marginsDelta.left()); + margins.setRight(margins.right() - _marginsDelta.right()); + margins.setBottom(margins.bottom() - _marginsDelta.bottom()); + margins.setTop(margins.top() - _marginsDelta.top()); + } else if (!_marginsDelta.isNull()) { + RECT w; + GetWindowRect(_handle, &w); + SetWindowPos( + _handle, + 0, + 0, + 0, + w.right - w.left - _marginsDelta.left() - _marginsDelta.right(), + w.bottom - w.top - _marginsDelta.top() - _marginsDelta.bottom(), + (SWP_NOMOVE + | SWP_NOSENDCHANGING + | SWP_NOZORDER + | SWP_NOACTIVATE + | SWP_NOREPOSITION)); + _marginsDelta = QMargins(); + } + + if (_isFullScreen || _title->isHidden()) { + margins = QMargins(); + if (_title->isHidden()) { + _marginsDelta = QMargins(); + } + } + if (const auto native = QGuiApplication::platformNativeInterface()) { + native->setWindowProperty( + window()->windowHandle()->handle(), + "WindowsCustomMargins", + QVariant::fromValue(margins)); + } +} + +void WindowHelper::fixMaximizedWindow() { + auto r = RECT(); + GetClientRect(_handle, &r); + const auto style = GetWindowLongPtr(_handle, GWL_STYLE); + const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE); + AdjustWindowRectEx(&r, style, false, styleEx); + if (style & WS_MAXIMIZE) { + auto w = RECT(); + GetWindowRect(_handle, &w); + if (const auto hMonitor = MonitorFromRect(&w, MONITOR_DEFAULTTONEAREST)) { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + const auto m = mi.rcWork; + SetWindowPos(_handle, 0, 0, 0, m.right - m.left - _marginsDelta.left() - _marginsDelta.right(), m.bottom - m.top - _marginsDelta.top() - _marginsDelta.bottom(), SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION); + } + } +} + HWND GetWindowHandle(not_null widget) { const auto toplevel = widget->window(); toplevel->createWinId(); diff --git a/ui/platform/win/ui_window_win.h b/ui/platform/win/ui_window_win.h index b5b1450..eada67c 100644 --- a/ui/platform/win/ui_window_win.h +++ b/ui/platform/win/ui_window_win.h @@ -51,10 +51,12 @@ public: private: void init(); + void updateMargins(); void updateWindowFrameColors(); void updateWindowFrameColors(bool active); void initialShadowUpdate(); void updateCornersRounding(); + void fixMaximizedWindow(); [[nodiscard]] bool handleNativeEvent( UINT msg, WPARAM wParam, @@ -79,6 +81,8 @@ private: rpl::event_stream _systemButtonDown; std::optional _shadow; rpl::variable _dpi; + QMargins _marginsDelta; + bool _updatingMargins = false; bool _isFullScreen = false; };