Use native window titlebar on Windows 11

This commit is contained in:
Ilya Fedin 2022-05-23 23:24:46 +04:00 committed by John Preston
parent 3255de2e5a
commit de5aa43f15
2 changed files with 121 additions and 14 deletions

View file

@ -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<RpWidget*> 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<QMargins> 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<QMargins> 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;
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::NativeFilter*> WindowHelper::GetNativeFilter() {
Expects(QCoreApplication::instance() != nullptr);

View file

@ -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<HitTestResult> _systemButtonDown;
std::optional<WindowShadow> _shadow;
rpl::variable<uint> _dpi;
rpl::variable<std::optional<QMargins>> _frameMargins;
bool _isFullScreen = false;
};