Use native window resize on Windows 11

This commit is contained in:
Ilya Fedin 2022-06-18 12:05:52 +04:00 committed by John Preston
parent 8e31adc0af
commit 4a41346a64
6 changed files with 108 additions and 44 deletions

View file

@ -280,18 +280,21 @@ void TitleControls::raise() {
}
HitTestResult TitleControls::hitTest(QPoint point, int padding) const {
const auto test = [&](
const object_ptr<AbstractButton> &button,
bool close) {
const auto test = [&](const object_ptr<AbstractButton> &button) {
return button && button->geometry().marginsAdded(
{ close ? padding : 0, padding, close ? padding : 0, 0 }
{ 0, padding, 0, 0 }
).contains(point);
};
if (test(_minimize, false)) {
if (::Platform::IsWindows11OrGreater()
&& !_maximizedState
&& (point.y() < style::ConvertScale(
window()->windowHandle()->devicePixelRatio()))) {
return HitTestResult::Top;
} else if (test(_minimize)) {
return HitTestResult::Minimize;
} else if (test(_maximizeRestore, false)) {
} else if (test(_maximizeRestore)) {
return HitTestResult::MaximizeRestore;
} else if (test(_close, true)) {
} else if (test(_close)) {
return HitTestResult::Close;
}
return HitTestResult::None;
@ -586,9 +589,9 @@ std::unique_ptr<SeparateTitleControls> SetupSeparateTitleControls(
controlsTop ? std::move(controlsTop) : rpl::single(0)
) | rpl::start_with_next([=](int width, int padding, int top) {
raw->wrap.setGeometry(
padding,
0,
top,
width - 2 * padding,
width,
raw->controls.geometry().height());
}, lifetime);

View file

@ -43,8 +43,7 @@ base::flat_map<HWND, not_null<WindowShadow*>> ShadowByHandle;
WindowShadow::WindowShadow(not_null<RpWidget*> window, QColor color)
: _window(window)
, _handle(GetWindowHandle(window))
, _windows11(::Platform::IsWindows11OrGreater()) {
, _handle(GetWindowHandle(window)) {
init(color);
}
@ -317,7 +316,7 @@ void WindowShadow::horCorners(int w, Gdiplus::Graphics *pgraphics0, Gdiplus::Gra
}
Gdiplus::Color WindowShadow::getColor(uchar alpha) const {
return Gdiplus::Color(BYTE(_windows11 ? 1 : alpha), _r, _g, _b);
return Gdiplus::Color(BYTE(alpha), _r, _g, _b);
}
Gdiplus::SolidBrush WindowShadow::getBrush(uchar alpha) const {

View file

@ -78,7 +78,6 @@ private:
const not_null<RpWidget*> _window;
const HWND _handle;
const bool _windows11 = false;
int _x = 0;
int _y = 0;

View file

@ -88,7 +88,7 @@ void TitleWidget::initInWindow(not_null<RpWindow*> window) {
) | rpl::filter([=](not_null<HitTestRequest*> request) {
return !isHidden() && geometry().contains(request->point);
}) | rpl::start_with_next([=](not_null<HitTestRequest*> request) {
request->result = hitTest(request->point);
request->result = hitTest(request->point, request->result);
}, lifetime());
SetupSemiNativeSystemButtons(&_controls, window, lifetime(), [=] {
@ -112,9 +112,9 @@ void TitleWidget::refreshGeometryWithWidth(int width) {
setGeometry(0, 0, width, _controls.st()->height + add);
if (_paddingHelper) {
_paddingHelper->controlsParent.setGeometry(
0,
add,
add,
width - 2 * add,
width,
_controls.st()->height);
}
update();
@ -140,7 +140,9 @@ void TitleWidget::resizeEvent(QResizeEvent *e) {
_shadow->setGeometry(0, height() - thickness, width(), thickness);
}
HitTestResult TitleWidget::hitTest(QPoint point) const {
HitTestResult TitleWidget::hitTest(
QPoint point,
HitTestResult oldResult) const {
const auto origin = _paddingHelper
? _paddingHelper->controlsParent.pos()
: QPoint();
@ -150,6 +152,8 @@ HitTestResult TitleWidget::hitTest(QPoint point) const {
const auto controlsResult = _controls.hitTest(point - origin, padding);
return (controlsResult != HitTestResult::None)
? controlsResult
: (oldResult != HitTestResult::Client)
? oldResult
: HitTestResult::Caption;
}

View file

@ -55,7 +55,9 @@ protected:
private:
struct PaddingHelper;
[[nodiscard]] HitTestResult hitTest(QPoint point) const;
[[nodiscard]] HitTestResult hitTest(
QPoint point,
HitTestResult oldResult) const;
[[nodiscard]] bool additionalPaddingRequired() const;
void refreshGeometryWithWidth(int width);
void setAdditionalPadding(int padding);

View file

@ -48,6 +48,13 @@ int(__stdcall *GetSystemMetricsForDpi)(
_In_ int nIndex,
_In_ UINT dpi);
BOOL(__stdcall *AdjustWindowRectExForDpi)(
_Inout_ LPRECT lpRect,
_In_ DWORD dwStyle,
_In_ BOOL bMenu,
_In_ DWORD dwExStyle,
_In_ UINT dpi);
[[nodiscard]] bool GetDpiForWindowSupported() {
static const auto Result = [&] {
#define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name)
@ -68,6 +75,16 @@ int(__stdcall *GetSystemMetricsForDpi)(
return Result;
}
[[nodiscard]] bool AdjustWindowRectExForDpiSupported() {
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, AdjustWindowRectExForDpi);
#undef LOAD_SYMBOL
}();
return Result;
}
[[nodiscard]] bool IsCompositionEnabled() {
auto result = BOOL(FALSE);
const auto success = (DwmIsCompositionEnabled(&result) == S_OK);
@ -217,7 +234,6 @@ WindowHelper::WindowHelper(not_null<RpWidget*> window)
, _handle(ResolveWindowHandle(window))
, _title(Ui::CreateChild<TitleWidget>(window.get()))
, _body(Ui::CreateChild<RpWidget>(window.get()))
, _shadow(std::in_place, window, st::windowShadowFg->c)
, _dpi(GetDpiForWindowSupported() ? GetDpiForWindow(_handle) : 0) {
Expects(_handle != nullptr);
@ -273,13 +289,14 @@ void WindowHelper::setNativeFrame(bool enabled) {
}
}
_title->setVisible(!enabled);
if (enabled) {
if (enabled || ::Platform::IsWindows11OrGreater()) {
_shadow.reset();
} else {
_shadow.emplace(window(), st::windowShadowFg->c);
_shadow->setResizeEnabled(!fixedSize());
initialShadowUpdate();
}
updateCornersRounding();
updateMargins();
updateWindowFrameColors();
fixMaximizedWindow();
@ -298,6 +315,9 @@ void WindowHelper::setNativeFrame(bool enabled) {
}
void WindowHelper::initialShadowUpdate() {
if (!_shadow) {
return;
}
using Change = WindowShadow::Change;
const auto noShadowStates = (Qt::WindowMinimized | Qt::WindowMaximized);
if ((window()->windowState() & noShadowStates) || window()->isHidden()) {
@ -305,7 +325,6 @@ void WindowHelper::initialShadowUpdate() {
} else {
_shadow->update(Change::Moved | Change::Resized | Change::Shown);
}
updateCornersRounding();
}
void WindowHelper::updateCornersRounding() {
@ -397,12 +416,41 @@ void WindowHelper::init() {
size.height() - (titleShown ? titleHeight : 0));
}, _body->lifetime());
hitTestRequests(
) | rpl::filter([=](not_null<HitTestRequest*> request) {
return ::Platform::IsWindows11OrGreater();
}) | rpl::start_with_next([=](not_null<HitTestRequest*> request) {
request->result = [=] {
RECT r{};
const auto style = GetWindowLongPtr(_handle, GWL_STYLE)
& ~WS_CAPTION;
const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE);
const auto dpi = style::ConvertScale(
96 * style::DevicePixelRatio());
if (AdjustWindowRectExForDpiSupported() && dpi) {
AdjustWindowRectExForDpi(&r, style, false, styleEx, dpi);
} else {
AdjustWindowRectEx(&r, style, false, styleEx);
}
const auto maximized = window()->isMaximized()
|| window()->isFullScreen();
return (!maximized && (request->point.y() < -r.top))
? HitTestResult::Top
: HitTestResult::Client;
}();
}, window()->lifetime());
updateMargins();
if (!::Platform::IsWindows11OrGreater()) {
_shadow.emplace(window(), st::windowShadowFg->c);
}
if (!::Platform::IsWindows8OrGreater()) {
SetWindowTheme(_handle, L" ", L" ");
QApplication::setStyle(QStyleFactory::create("Windows"));
}
updateWindowFrameColors();
const auto handleStateChanged = [=](Qt::WindowState state) {
@ -427,6 +475,7 @@ void WindowHelper::init() {
handleStateChanged);
initialShadowUpdate();
updateCornersRounding();
auto dm = std::make_unique<DirectManipulation>(window());
if (dm->valid()) {
@ -506,11 +555,16 @@ bool WindowHelper::handleNativeEvent(
if (_title->isHidden() || window()->isFullScreen() || !wParam) {
return false;
}
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(_handle, &wp)
&& (wp.showCmd == SW_SHOWMAXIMIZED)) {
const auto r = &((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0];
const auto r = &((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0];
const auto maximized = [&] {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
return GetWindowPlacement(_handle, &wp)
&& (wp.showCmd == SW_SHOWMAXIMIZED);
}();
const auto addBorders = maximized
|| ::Platform::IsWindows11OrGreater();
if (addBorders) {
const auto dpi = _dpi.current();
const auto borderWidth = (GetSystemMetricsForDpiSupported() && dpi)
? GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi)
@ -519,8 +573,12 @@ bool WindowHelper::handleNativeEvent(
+ GetSystemMetrics(SM_CXPADDEDBORDER);
r->left += borderWidth;
r->right -= borderWidth;
r->top += borderWidth;
if (maximized) {
r->top += borderWidth;
}
r->bottom -= borderWidth;
}
if (maximized) {
const auto hMonitor = MonitorFromWindow(
_handle,
MONITOR_DEFAULTTONEAREST);
@ -536,10 +594,8 @@ bool WindowHelper::handleNativeEvent(
case ABE_BOTTOM: r->bottom -= 1; break;
}
}
if (result) *result = 0;
} else {
if (result) *result = WVR_REDRAW;
}
if (result) *result = addBorders ? 0 : WVR_REDRAW;
} return true;
case WM_NCRBUTTONUP: {
@ -569,15 +625,15 @@ bool WindowHelper::handleNativeEvent(
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED: {
auto placement = WINDOWPLACEMENT{
.length = sizeof(WINDOWPLACEMENT),
};
if (!GetWindowPlacement(_handle, &placement)) {
LOG(("System Error: GetWindowPlacement failed."));
return false;
}
_title->refreshAdditionalPaddings(_handle, placement);
if (_shadow) {
auto placement = WINDOWPLACEMENT{
.length = sizeof(WINDOWPLACEMENT),
};
if (!GetWindowPlacement(_handle, &placement)) {
LOG(("System Error: GetWindowPlacement failed."));
return false;
}
_title->refreshAdditionalPaddings(_handle, placement);
if (placement.showCmd == SW_SHOWMAXIMIZED
|| placement.showCmd == SW_SHOWMINIMIZED) {
_shadow->update(WindowShadow::Change::Hidden);
@ -606,8 +662,8 @@ bool WindowHelper::handleNativeEvent(
window()->windowHandle()->windowStateChanged(state);
}
updateMargins();
_title->refreshAdditionalPaddings(_handle);
if (_shadow) {
_title->refreshAdditionalPaddings(_handle);
const auto changes = (wParam == SIZE_MINIMIZED
|| wParam == SIZE_MAXIMIZED)
? WindowShadow::Change::Hidden
@ -630,8 +686,8 @@ bool WindowHelper::handleNativeEvent(
} return false;
case WM_MOVE: {
_title->refreshAdditionalPaddings(_handle);
if (_shadow) {
_title->refreshAdditionalPaddings(_handle);
_shadow->update(WindowShadow::Change::Moved);
}
} return false;
@ -647,9 +703,9 @@ bool WindowHelper::handleNativeEvent(
const auto mapped = QPoint(
int(std::floor(p.x / ratio)),
int(std::floor(p.y / ratio)));
*result = [&] {
*result = [&]() -> LRESULT {
if (!window()->rect().contains(mapped)) {
return HTTRANSPARENT;
return DefWindowProc(_handle, msg, wParam, lParam);
}
auto request = HitTestRequest{
.point = mapped,
@ -672,7 +728,7 @@ bool WindowHelper::handleNativeEvent(
case HitTestResult::Close: return systemButtonHitTest(result);
case HitTestResult::None:
default: return HTTRANSPARENT;
default: return DefWindowProc(_handle, msg, wParam, lParam);
};
}();
_systemButtonOver.fire(systemButtonHitTest(*result));
@ -814,7 +870,7 @@ void WindowHelper::updateWindowFrameColors(bool active) {
}
void WindowHelper::updateMargins() {
if (_updatingMargins) return;
if (::Platform::IsWindows11OrGreater() || _updatingMargins) return;
_updatingMargins = true;
const auto guard = gsl::finally([&] { _updatingMargins = false; });
@ -888,6 +944,7 @@ void WindowHelper::updateMargins() {
}
void WindowHelper::fixMaximizedWindow() {
if (::Platform::IsWindows11OrGreater()) return;
auto r = RECT();
GetClientRect(_handle, &r);
const auto style = GetWindowLongPtr(_handle, GWL_STYLE);