From 220d6835283c51c6e529ad5ef2f100703098e270 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 17 Jan 2022 13:37:14 +0300 Subject: [PATCH] Fix Windows 11 title controls by additional padding. --- ui/platform/win/ui_window_title_win.cpp | 154 +++++++++++++++++++++++- ui/platform/win/ui_window_title_win.h | 20 +++ ui/platform/win/ui_window_win.cpp | 18 ++- 3 files changed, 182 insertions(+), 10 deletions(-) diff --git a/ui/platform/win/ui_window_title_win.cpp b/ui/platform/win/ui_window_title_win.cpp index 1e3e63d..414adcd 100644 --- a/ui/platform/win/ui_window_title_win.cpp +++ b/ui/platform/win/ui_window_title_win.cpp @@ -6,9 +6,13 @@ // #include "ui/platform/win/ui_window_title_win.h" +#include "ui/platform/win/ui_window_win.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/ui_utility.h" +#include "base/platform/base_platform_info.h" +#include "base/platform/win/base_windows_safe_library.h" +#include "base/debug_log.h" #include "styles/style_widgets.h" #include "styles/palette.h" @@ -16,28 +20,75 @@ #include #include +#include +#include + namespace Ui { namespace Platform { +namespace { + +HRESULT(__stdcall *GetScaleFactorForMonitor)( + _In_ HMONITOR hMon, + _Out_ DEVICE_SCALE_FACTOR *pScale); + +[[nodiscard]] bool ScaleQuerySupported() { + static const auto Result = [&] { +#define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name) + const auto shcore = base::Platform::SafeLoadLibrary(L"Shcore.dll"); + return LOAD_SYMBOL(shcore, GetScaleFactorForMonitor); +#undef LOAD_SYMBOL + }(); + return Result; +} + +} // namespace + +struct TitleWidget::PaddingHelper { + explicit PaddingHelper(QWidget *parent) : controlsParent(parent) { + } + + RpWidget controlsParent; + int padding = 0; +}; TitleWidget::TitleWidget(not_null parent) : RpWidget(parent) -, _controls(this, st::defaultWindowTitle) +, _paddingHelper(CheckTitlePaddingRequired() + ? std::make_unique(this) + : nullptr) +, _controls( + _paddingHelper ? &_paddingHelper->controlsParent : this, + st::defaultWindowTitle) , _shadow(this, st::titleShadow) { setAttribute(Qt::WA_OpaquePaintEvent); parent->widthValue( ) | rpl::start_with_next([=](int width) { - setGeometry(0, 0, width, _controls.st()->height); + refreshGeometryWithWidth(width); }, lifetime()); } +TitleWidget::~TitleWidget() = default; + void TitleWidget::setText(const QString &text) { window()->setWindowTitle(text); } void TitleWidget::setStyle(const style::WindowTitle &st) { _controls.setStyle(st); - setGeometry(0, 0, window()->width(), _controls.st()->height); + refreshGeometryWithWidth(window()->width()); +} + +void TitleWidget::refreshGeometryWithWidth(int width) { + const auto add = _paddingHelper ? _paddingHelper->padding : 0; + setGeometry(0, 0, width, _controls.st()->height + add); + if (_paddingHelper) { + _paddingHelper->controlsParent.setGeometry( + add, + add, + width - 2 * add, + _controls.st()->height); + } update(); } @@ -57,7 +108,8 @@ void TitleWidget::paintEvent(QPaintEvent *e) { } void TitleWidget::resizeEvent(QResizeEvent *e) { - _shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth); + const auto thickness = st::lineWidth; + _shadow->setGeometry(0, height() - thickness, width(), thickness); } HitTestResult TitleWidget::hitTest(QPoint point) const { @@ -69,5 +121,99 @@ HitTestResult TitleWidget::hitTest(QPoint point) const { return HitTestResult::None; } +bool TitleWidget::additionalPaddingRequired() const { + return _paddingHelper && !isHidden(); +} + +void TitleWidget::refreshAdditionalPaddings() { + if (!additionalPaddingRequired()) { + return; + } + const auto handle = GetWindowHandle(this); + if (!handle) { + LOG(("System Error: GetWindowHandle failed.")); + return; + } + refreshAdditionalPaddings(handle); +} + +void TitleWidget::refreshAdditionalPaddings(HWND handle) { + if (!additionalPaddingRequired()) { + return; + } + auto placement = WINDOWPLACEMENT{ + .length = sizeof(WINDOWPLACEMENT), + }; + if (!GetWindowPlacement(handle, &placement)) { + LOG(("System Error: GetWindowPlacement failed.")); + return; + } + refreshAdditionalPaddings(handle, placement); +} + +void TitleWidget::refreshAdditionalPaddings( + HWND handle, + const WINDOWPLACEMENT &placement) { + auto geometry = RECT(); + if (!GetWindowRect(handle, &geometry)) { + LOG(("System Error: GetWindowRect failed.")); + return; + } + const auto normal = placement.rcNormalPosition; + const auto rounded = (normal.left == geometry.left) + && (normal.right == geometry.right) + && (normal.top == geometry.top) + && (normal.bottom == geometry.bottom); + const auto padding = [&] { + if (!rounded) { + return 0; + } + const auto monitor = MonitorFromWindow( + handle, + MONITOR_DEFAULTTONEAREST); + if (!monitor) { + LOG(("System Error: MonitorFromWindow failed.")); + return -1; + } + auto factor = DEVICE_SCALE_FACTOR(); + if (!SUCCEEDED(GetScaleFactorForMonitor(monitor, &factor))) { + LOG(("System Error: GetScaleFactorForMonitor failed.")); + return -1; + } else if (factor < 100 || factor > 500) { + LOG(("System Error: Bad scale factor %1.").arg(int(factor))); + return -1; + } + const auto pixels = (factor + 50) / 100; + return int(base::SafeRound(pixels / window()->devicePixelRatioF())); + }(); + if (padding < 0) { + return; + } + setAdditionalPadding(padding); +} + +void TitleWidget::setAdditionalPadding(int padding) { + Expects(_paddingHelper != nullptr); + + if (_paddingHelper->padding == padding) { + return; + } + _paddingHelper->padding = padding; + refreshGeometryWithWidth(window()->width()); +} + +void TitleWidget::setVisibleHook(bool visible) { + RpWidget::setVisibleHook(visible); + if (additionalPaddingRequired()) { + PostponeCall(this, [=] { + refreshAdditionalPaddings(); + }); + } +} + +bool CheckTitlePaddingRequired() { + return ::Platform::IsWindows11OrGreater() && ScaleQuerySupported(); +} + } // namespace Platform } // namespace Ui diff --git a/ui/platform/win/ui_window_title_win.h b/ui/platform/win/ui_window_title_win.h index c6e7d0e..78cb665 100644 --- a/ui/platform/win/ui_window_title_win.h +++ b/ui/platform/win/ui_window_title_win.h @@ -13,6 +13,8 @@ #include #include +#include // HWND, WINDOWPLACEMENT + namespace style { struct WindowTitle; } // namespace style @@ -42,6 +44,7 @@ enum class HitTestResult { class TitleWidget : public RpWidget { public: explicit TitleWidget(not_null parent); + ~TitleWidget(); void setText(const QString &text); void setStyle(const style::WindowTitle &st); @@ -49,15 +52,32 @@ public: [[nodiscard]] HitTestResult hitTest(QPoint point) const; void setResizeEnabled(bool enabled); + void refreshAdditionalPaddings(); + void refreshAdditionalPaddings(HWND handle); + void refreshAdditionalPaddings( + HWND handle, + const WINDOWPLACEMENT &placement); + protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void setVisibleHook(bool visible) override; + private: + struct PaddingHelper; + + [[nodiscard]] bool additionalPaddingRequired() const; + void refreshGeometryWithWidth(int width); + void setAdditionalPadding(int padding); + + std::unique_ptr _paddingHelper; TitleControls _controls; object_ptr _shadow; }; +[[nodiscard]] bool CheckTitlePaddingRequired(); + } // namespace Platform } // namespace Ui diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index c0d78dd..f888f11 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -9,7 +9,6 @@ #include "ui/inactive_press.h" #include "ui/platform/win/ui_window_title_win.h" #include "base/platform/base_platform_info.h" -#include "base/platform/win/base_windows_safe_library.h" #include "base/integration.h" #include "base/debug_log.h" #include "styles/palette.h" @@ -409,11 +408,16 @@ bool WindowHelper::handleNativeEvent( case WM_WINDOWPOSCHANGING: case WM_WINDOWPOSCHANGED: { if (_shadow) { - WINDOWPLACEMENT wp; - wp.length = sizeof(WINDOWPLACEMENT); - if (GetWindowPlacement(_handle, &wp) - && (wp.showCmd == SW_SHOWMAXIMIZED - || wp.showCmd == SW_SHOWMINIMIZED)) { + 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); } else { _shadow->update( @@ -439,6 +443,7 @@ bool WindowHelper::handleNativeEvent( } updateMargins(); if (_shadow) { + _title->refreshAdditionalPaddings(_handle); const auto changes = (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXIMIZED) ? WindowShadow::Change::Hidden @@ -462,6 +467,7 @@ bool WindowHelper::handleNativeEvent( case WM_MOVE: { if (_shadow) { + _title->refreshAdditionalPaddings(_handle); _shadow->update(WindowShadow::Change::Moved); } } return false;