Use native window titlebar on Windows 11
This commit is contained in:
parent
3255de2e5a
commit
de5aa43f15
2 changed files with 121 additions and 14 deletions
|
|
@ -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;
|
||||
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::NativeFilter*> WindowHelper::GetNativeFilter() {
|
||||
Expects(QCoreApplication::instance() != nullptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue