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