diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index 7bf3171..c2c8152 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -9,6 +9,7 @@ #include "ui/inactive_press.h" #include "ui/platform/win/ui_window_title_win.h" #include "ui/widgets/rp_window.h" +#include "ui/painter.h" #include "base/platform/win/base_windows_safe_library.h" #include "base/platform/base_platform_info.h" #include "base/integration.h" @@ -206,7 +207,9 @@ not_null WindowHelper::body() { } QMargins WindowHelper::frameMargins() { - return _title->isHidden() + return frameMarginsSet() + ? *_frameMargins.current() + : _title->isHidden() ? BasicWindowHelper::nativeFrameMargins() : QMargins{ 0, _title->height(), 0, 0 }; } @@ -242,6 +245,14 @@ void WindowHelper::setNativeFrame(bool enabled) { GWL_STYLE, enabled ? (style | WS_CAPTION) : (style & ~WS_CAPTION)); } + if (::Platform::IsWindows11OrGreater()) { + if (enabled) { + _frameMargins = QMargins(); + updateFrameMargins(); + } else { + _frameMargins = std::nullopt; + } + } _title->setVisible(!enabled); if (enabled || ::Platform::IsWindows11OrGreater()) { _shadow.reset(); @@ -352,16 +363,24 @@ void WindowHelper::init() { rpl::combine( window()->sizeValue(), _title->heightValue(), - _title->shownValue() + _title->shownValue(), + _frameMargins.value() ) | rpl::start_with_next([=]( QSize size, int titleHeight, - bool titleShown) { - _body->setGeometry( + bool titleShown, + std::optional frameMargins) { + _body->setGeometry(QRect( + 0, + 0, + size.width(), + size.height() + ).marginsRemoved(QMargins( 0, titleShown ? titleHeight : 0, - size.width(), - size.height() - (titleShown ? titleHeight : 0)); + 0, + 0 + )).marginsRemoved(frameMargins ? *frameMargins : QMargins())); }, _body->lifetime()); hitTestRequests( @@ -373,21 +392,64 @@ void WindowHelper::init() { const auto style = GetWindowLongPtr(_handle, GWL_STYLE) & ~WS_CAPTION; const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE); - const auto dpi = style::ConvertScale( - 96 * style::DevicePixelRatio()); + const auto dpi = frameMarginsSet() + ? _dpi.current() + : style::ConvertScale(96 * style::DevicePixelRatio()); if (AdjustWindowRectExForDpiSupported() && dpi) { AdjustWindowRectExForDpi(&r, style, false, styleEx, dpi); } else { AdjustWindowRectEx(&r, style, false, styleEx); } + const auto frameMargins = _frameMargins.current() + ? *_frameMargins.current() + : QMargins(); const auto maximized = window()->isMaximized() || window()->isFullScreen(); return (!maximized && (request->point.y() < -r.top)) ? HitTestResult::Top + : (request->point.y() < frameMargins.top()) + ? HitTestResult::Caption : HitTestResult::Client; }(); }, window()->lifetime()); + _dpi.value( + ) | rpl::start_with_next([=](uint dpi) { + updateFrameMargins(); + }, window()->lifetime()); + + _frameMargins.value( + ) | rpl::start_with_next([=](std::optional frameMargins) { + const auto deviceDependentMargins = frameMargins + ? *frameMargins * window()->devicePixelRatioF() + : QMargins(); + const MARGINS m{ + deviceDependentMargins.left(), + deviceDependentMargins.right(), + deviceDependentMargins.top(), + deviceDependentMargins.bottom(), + }; + DwmExtendFrameIntoClientArea(_handle, &m); + }, window()->lifetime()); + + window()->paintRequest( + ) | rpl::filter([=](QRect clip) { + return frameMarginsSet() && clip.intersects(QRect( + 0, + 0, + window()->width(), + _frameMargins.current()->top())); + }) | rpl::start_with_next([=] { + Painter p(window()); + p.setCompositionMode(QPainter::CompositionMode_Clear); + p.fillRect( + 0, + 0, + window()->width(), + _frameMargins.current()->top(), + Qt::black); + }, window()->lifetime()); + if (!::Platform::IsWindows11OrGreater()) { _shadow.emplace(window(), st::windowShadowFg->c); } @@ -451,7 +513,9 @@ bool WindowHelper::handleNativeEvent( } return true; case WM_NCCALCSIZE: { - if (_title->isHidden() || window()->isFullScreen() || !wParam) { + if ((_title->isHidden() && !frameMarginsSet()) + || window()->isFullScreen() + || !wParam) { return false; } const auto r = &((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0]; @@ -474,7 +538,7 @@ bool WindowHelper::handleNativeEvent( - ((style & WS_CAPTION) ? 0 : 1); r->left += borderWidth; r->right -= borderWidth; - if (maximized) { + if (maximized && !frameMarginsSet()) { r->top += borderWidth; } r->bottom -= borderWidth; @@ -491,7 +555,9 @@ bool WindowHelper::handleNativeEvent( switch (uEdge) { case ABE_LEFT: r->left += 1; break; case ABE_RIGHT: r->right -= 1; break; - case ABE_TOP: r->top += 1; break; + if (!frameMarginsSet()) { + case ABE_TOP: r->top += 1; break; + } case ABE_BOTTOM: r->bottom -= 1; break; } } @@ -500,7 +566,7 @@ bool WindowHelper::handleNativeEvent( } return true; case WM_NCRBUTTONUP: { - if (_title->isHidden()) { + if (_title->isHidden() && !frameMarginsSet()) { return false; } SendMessage(_handle, 0x313 /* WM_POPUPSYSTEMMENU */, 0, lParam); @@ -608,7 +674,16 @@ bool WindowHelper::handleNativeEvent( } return false; case WM_NCHITTEST: { - if (!result || _title->isHidden()) { + if (!result) { + return false; + } else if (frameMarginsSet()) { + LRESULT lRet = 0; + DwmDefWindowProc(_handle, msg, wParam, lParam, &lRet); + if (lRet) { + *result = lRet; + return true; + } + } else if (_title->isHidden()) { return false; } @@ -646,6 +721,12 @@ bool WindowHelper::handleNativeEvent( _systemButtonOver.fire(systemButtonHitTest(*result)); } return true; + case WM_NCMOUSELEAVE: { + if (result && frameMarginsSet()) { + return DwmDefWindowProc(_handle, msg, wParam, lParam, result); + } + } return false; + // should return true for Qt not to change window size // when moving the window between screens // change to false once runtime scale change would be supported @@ -743,7 +824,11 @@ HitTestResult WindowHelper::systemButtonHitTest(int result) const { } int WindowHelper::titleHeight() const { - return _title->isHidden() ? 0 : _title->height(); + return frameMarginsSet() + ? _frameMargins.current()->top() + : _title->isHidden() + ? 0 + : _title->height(); } void WindowHelper::updateWindowFrameColors() { @@ -774,6 +859,25 @@ void WindowHelper::updateWindowFrameColors(bool active) { sizeof(COLORREF)); } +bool WindowHelper::frameMarginsSet() const { + return _frameMargins.current() && !_frameMargins.current()->isNull(); +} + +void WindowHelper::updateFrameMargins() { + if (!_frameMargins.current()) { + return; + } + RECT r{}; + const auto style = GetWindowLongPtr(_handle, GWL_STYLE); + const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE); + if (AdjustWindowRectExForDpiSupported() && _dpi.current()) { + AdjustWindowRectExForDpi(&r, style, false, styleEx, _dpi.current()); + } else { + AdjustWindowRectEx(&r, style, false, styleEx); + } + _frameMargins = QMargins(0, -r.top, 0, 0) / window()->devicePixelRatioF(); +} + not_null WindowHelper::GetNativeFilter() { Expects(QCoreApplication::instance() != nullptr); diff --git a/ui/platform/win/ui_window_win.h b/ui/platform/win/ui_window_win.h index 0f823b4..5cd022e 100644 --- a/ui/platform/win/ui_window_win.h +++ b/ui/platform/win/ui_window_win.h @@ -49,6 +49,7 @@ private: friend class NativeFilter; void init(); + void updateFrameMargins(); void updateWindowFrameColors(); void updateWindowFrameColors(bool active); void initialShadowUpdate(); @@ -64,6 +65,7 @@ private: LPARAM lParam, LRESULT *result); [[nodiscard]] bool fixedSize() const; + [[nodiscard]] bool frameMarginsSet() const; [[nodiscard]] int systemButtonHitTest(HitTestResult result) const; [[nodiscard]] HitTestResult systemButtonHitTest(int result) const; @@ -78,6 +80,7 @@ private: rpl::event_stream _systemButtonDown; std::optional _shadow; rpl::variable _dpi; + rpl::variable> _frameMargins; bool _isFullScreen = false; };