From 68bc4c31edce3a56050da851879b67abe29941a5 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Fri, 27 Nov 2020 18:52:26 +0800 Subject: [PATCH] Cleanup WinNativeEventFilter: 1. Removed all unused functions and macros. Win32 API prototypes are not touched because we may still need them in the future. 2. Don't store data through SetWindowLongPtrW, using dynamic properties of QWindow instead. People may rely on GetWindowLongPtrW(hwnd, GWLP_USERDATA) to do their own work. 3. Some helper functions are removed, such as moveWindowToDesktopCenter(). You can implement them yourself. Keep them in the source code will keep pulling in new dependencies so I have to get rid of them. 4. Some blocks are removed, such as WM_GETMINMAXIINFO. It's not needed. You can call Qt's standard API now, such as setMaximumSize() and setMinimumSize(). FramelessQuickHelper: 1. Removed all functions that can be replaced by Qt's own API, such as setMaximumSize() and setMinimumSize(). 2. Removed helper functions, such as moveWindowToDesktopCenter(). You can implement them yourself. Just few lines of code. FramelessWindowsManager: Adapt to the above changes. Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- examples/Win32Demo/Win32Demo.pro | 2 +- examples/Win32Demo/widget.cpp | 108 ++- examples/Win32Demo/widget.h | 4 +- framelessquickhelper.cpp | 66 +- framelessquickhelper.h | 25 +- framelesswindowsmanager.cpp | 172 +--- framelesswindowsmanager.h | 17 +- winnativeeventfilter.cpp | 1290 ++++++------------------------ winnativeeventfilter.h | 91 +-- 9 files changed, 357 insertions(+), 1418 deletions(-) diff --git a/examples/Win32Demo/Win32Demo.pro b/examples/Win32Demo/Win32Demo.pro index 6033dfe..f03ae09 100644 --- a/examples/Win32Demo/Win32Demo.pro +++ b/examples/Win32Demo/Win32Demo.pro @@ -1,6 +1,6 @@ TARGET = Win32Demo TEMPLATE = app -QT += widgets +QT += gui-private widgets HEADERS += widget.h SOURCES += widget.cpp main.cpp include($$PWD/../common.pri) diff --git a/examples/Win32Demo/widget.cpp b/examples/Win32Demo/widget.cpp index cdd52ec..dcd62cb 100644 --- a/examples/Win32Demo/widget.cpp +++ b/examples/Win32Demo/widget.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,16 @@ #include #include #include +#include #include +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#else +#include +#include +#endif + +Q_DECLARE_METATYPE(QMargins) // Some old SDK doesn't have this value. #ifndef WM_DPICHANGED @@ -57,7 +67,6 @@ QColor g_cColorizationColor = Qt::white; const char g_sUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR"; const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME"; const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10"; -const char g_sDontExtendFrame[] = "WNEF_DO_NOT_EXTEND_FRAME"; const QLatin1String g_sSystemButtonsStyleSheet(R"( #iconButton, #minimizeButton, #maximizeButton, #closeButton { @@ -130,6 +139,27 @@ const QLatin1String g_sMaximizeButtonImageLight(":/images/button_maximize_white. const QLatin1String g_sRestoreButtonImageLight(":/images/button_restore_white.svg"); const QLatin1String g_sCloseButtonImageLight(":/images/button_close_white.svg"); +void updateQtFrame(const QWindow *window, const int tbh) +{ + Q_ASSERT(window); + QMargins margins = {0, -tbh, 0, 0}; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QPlatformWindow *platformWindow = window->handle(); + if (platformWindow) { + QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow, + QString::fromUtf8( + "WindowsCustomMargins"), + QVariant::fromValue(margins)); + } +#else + auto *platformWindow = dynamic_cast( + window->handle()); + if (platformWindow) { + platformWindow->setCustomMargins(margins); + } +#endif +} + } // namespace Widget::Widget(QWidget *parent) : QWidget(parent) @@ -138,6 +168,18 @@ Widget::Widget(QWidget *parent) : QWidget(parent) initializeWindow(); } +void Widget::triggerFrameChange() +{ + SetWindowPos(reinterpret_cast(windowHandle()->winId()), + nullptr, + 0, + 0, + 0, + 0, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER + | SWP_NOOWNERZORDER); +} + void Widget::retranslateUi() { setWindowTitle(tr("Widget")); @@ -152,7 +194,6 @@ void Widget::retranslateUi() extendToTitleBarCB->setText(tr("Extend to title bar")); forceAcrylicCB->setText(tr("Force enabling Acrylic effect")); resizableCB->setText(tr("Resizable")); - moveCenterButton->setText(tr("Move to desktop center")); } void Widget::setupUi() @@ -168,7 +209,8 @@ void Widget::setupUi() titleBarWidget->setSizePolicy(sizePolicy); const int titleBarHeight = WinNativeEventFilter::getSystemMetric(windowHandle(), - WinNativeEventFilter::SystemMetric::TitleBarHeight); + WinNativeEventFilter::SystemMetric::TitleBarHeight, + false); titleBarWidget->setMinimumSize(QSize(0, titleBarHeight)); titleBarWidget->setMaximumSize(QSize(16777215, titleBarHeight)); horizontalLayout = new QHBoxLayout(titleBarWidget); @@ -282,9 +324,6 @@ void Widget::setupUi() horizontalLayout_3 = new QHBoxLayout(); horizontalSpacer_5 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout_3->addSpacerItem(horizontalSpacer_5); - moveCenterButton = new QPushButton(contentsWidget); - moveCenterButton->setFont(font1); - horizontalLayout_3->addWidget(moveCenterButton); horizontalSpacer_6 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout_3->addSpacerItem(horizontalSpacer_6); verticalLayout_2->addLayout(horizontalLayout_3); @@ -361,7 +400,6 @@ bool Widget::eventFilter(QObject *object, QEvent *event) } } updateTitleBar(); - moveCenterButton->setEnabled(isNormaled()); break; } case QEvent::WinIdChange: @@ -390,25 +428,14 @@ bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *resul if (customizeTitleBarCB && customizeTitleBarCB->isChecked()) { const auto msg = static_cast(message); switch (msg->message) { - case WM_NCRBUTTONUP: { - if (msg->wParam == HTCAPTION) { - if (WinNativeEventFilter::displaySystemMenu(windowHandle())) { - *result = 0; - return true; - } - } - break; - } case WM_DWMCOLORIZATIONCOLORCHANGED: { g_cColorizationColor = QColor::fromRgba(msg->wParam); if (shouldDrawThemedBorder()) { - updateWindow(); + update(); } break; } case WM_DPICHANGED: - updateWindow(); - break; case WM_NCPAINT: update(); break; @@ -434,13 +461,6 @@ void Widget::paintEvent(QPaintEvent *event) } } -void Widget::updateWindow() -{ - WinNativeEventFilter::updateFrameMargins(windowHandle()); - WinNativeEventFilter::updateWindow(windowHandle(), true, true); - update(); -} - void Widget::updateTitleBar() { const bool themedTitleBar = shouldDrawThemedTitleBar() && isActiveWindow(); @@ -494,7 +514,7 @@ void Widget::initializeOptions() if (m_bIsWin10OrGreater) { //preserveWindowFrameCB->click(); if (m_bCanAcrylicBeEnabled) { - //forceAcrylicCB->click(); + forceAcrylicCB->click(); } } customizeTitleBarCB->click(); @@ -515,43 +535,42 @@ void Widget::setupConnections() } }); connect(closeButton, &QPushButton::clicked, this, &Widget::close); - connect(moveCenterButton, &QPushButton::clicked, this, [this]() { - WinNativeEventFilter::moveWindowToDesktopCenter(windowHandle()); - }); connect(this, &Widget::windowTitleChanged, titleLabel, &QLabel::setText); connect(this, &Widget::windowIconChanged, iconButton, &QPushButton::setIcon); connect(customizeTitleBarCB, &QCheckBox::stateChanged, this, [this](int state) { const bool enable = state == Qt::Checked; preserveWindowFrameCB->setEnabled(enable); - WinNativeEventFilter::updateQtFrame(windowHandle(), - enable ? WinNativeEventFilter::getSystemMetric( - windowHandle(), - WinNativeEventFilter::SystemMetric::TitleBarHeight) - : 0); + updateQtFrame(windowHandle(), + enable ? WinNativeEventFilter::getSystemMetric( + windowHandle(), + WinNativeEventFilter::SystemMetric::TitleBarHeight, + true, + true) + : 0); titleBarWidget->setVisible(enable); if (enable) { qunsetenv(g_sUseNativeTitleBar); } else { qputenv(g_sUseNativeTitleBar, "1"); } - updateWindow(); + triggerFrameChange(); + update(); }); connect(preserveWindowFrameCB, &QCheckBox::stateChanged, this, [this](int state) { const bool enable = state == Qt::Checked; if (enable) { qputenv(g_sPreserveWindowFrame, "1"); - qputenv(g_sDontExtendFrame, "1"); } else { qunsetenv(g_sPreserveWindowFrame); - qunsetenv(g_sDontExtendFrame); } if (!enable && shouldDrawBorder()) { layout()->setContentsMargins(1, 1, 1, 1); } else { layout()->setContentsMargins(0, 0, 0, 0); } + triggerFrameChange(); updateTitleBar(); - updateWindow(); + update(); }); connect(blurEffectCB, &QCheckBox::stateChanged, this, [this](int state) { const bool enable = state == Qt::Checked; @@ -575,7 +594,7 @@ void Widget::setupConnections() } setPalette(palette); WinNativeEventFilter::setBlurEffectEnabled(windowHandle(), enable, color); - updateWindow(); + update(); if (useAcrylicEffect && enable && WinNativeEventFilter::isTransparencyEffectEnabled()) { QMessageBox::warning(this, tr("BUG Warning!"), @@ -606,15 +625,16 @@ void Widget::setupConnections() connect(resizableCB, &QCheckBox::stateChanged, this, [this](int state) { const bool enable = state == Qt::Checked; maximizeButton->setEnabled(enable); - WinNativeEventFilter::setWindowResizable(windowHandle(), enable); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, !enable); + show(); }); } void Widget::initializeFramelessFunctions() { - WinNativeEventFilter::WINDOWDATA data = {}; - data.ignoreObjects << iconButton << minimizeButton << maximizeButton << closeButton; - WinNativeEventFilter::addFramelessWindow(windowHandle(), &data); + WinNativeEventFilter::addFramelessWindow(windowHandle()); + WinNativeEventFilter::setIgnoredObjects(windowHandle(), + {minimizeButton, maximizeButton, closeButton}); installEventFilter(this); } diff --git a/examples/Win32Demo/widget.h b/examples/Win32Demo/widget.h index 3b9aa59..b424eeb 100644 --- a/examples/Win32Demo/widget.h +++ b/examples/Win32Demo/widget.h @@ -94,13 +94,13 @@ protected: private: void setupUi(); - void updateWindow(); void updateTitleBar(); void initializeOptions(); void setupConnections(); void initializeFramelessFunctions(); void initializeVariables(); void initializeWindow(); + void triggerFrameChange(); private: bool m_bIsWin10OrGreater = false, m_bCanAcrylicBeEnabled = false, m_bExtendToTitleBar = false, @@ -115,7 +115,7 @@ private: *verticalSpacer = nullptr, *horizontalSpacer_5 = nullptr, *horizontalSpacer_6 = nullptr; QPushButton *iconButton = nullptr, *minimizeButton = nullptr, *maximizeButton = nullptr, - *closeButton = nullptr, *moveCenterButton = nullptr; + *closeButton = nullptr; QLabel *titleLabel = nullptr; QCheckBox *customizeTitleBarCB = nullptr, *preserveWindowFrameCB = nullptr, *blurEffectCB = nullptr, *extendToTitleBarCB = nullptr, *forceAcrylicCB = nullptr, diff --git a/framelessquickhelper.cpp b/framelessquickhelper.cpp index a50d1cc..56022fe 100644 --- a/framelessquickhelper.cpp +++ b/framelessquickhelper.cpp @@ -35,7 +35,6 @@ namespace { const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME"; -const char g_sDontExtendFrame[] = "WNEF_DO_NOT_EXTEND_FRAME"; const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10"; } // namespace @@ -92,17 +91,6 @@ void FramelessQuickHelper::setResizable(const bool val) Q_EMIT resizableChanged(val); } -bool FramelessQuickHelper::titleBarEnabled() const -{ - return FramelessWindowsManager::getTitleBarEnabled(window()); -} - -void FramelessQuickHelper::setTitleBarEnabled(const bool val) -{ - FramelessWindowsManager::setTitleBarEnabled(window(), val); - Q_EMIT titleBarEnabledChanged(val); -} - #ifdef Q_OS_WINDOWS bool FramelessQuickHelper::canHaveWindowFrame() const { @@ -145,46 +133,9 @@ bool FramelessQuickHelper::transparencyEffectEnabled() const } #endif -QSize FramelessQuickHelper::minimumSize() const +void FramelessQuickHelper::removeWindowFrame() { - return FramelessWindowsManager::getMinimumSize(window()); -} - -void FramelessQuickHelper::setMinimumSize(const QSize &val) -{ - FramelessWindowsManager::setMinimumSize(window(), val); - Q_EMIT minimumSizeChanged(val); -} - -QSize FramelessQuickHelper::maximumSize() const -{ - return FramelessWindowsManager::getMaximumSize(window()); -} - -void FramelessQuickHelper::setMaximumSize(const QSize &val) -{ - FramelessWindowsManager::setMaximumSize(window(), val); - Q_EMIT maximumSizeChanged(val); -} - -void FramelessQuickHelper::removeWindowFrame(const bool center) -{ - FramelessWindowsManager::addWindow(window(), center); -} - -void FramelessQuickHelper::moveWindowToDesktopCenter() -{ - FramelessWindowsManager::moveWindowToDesktopCenter(window()); -} - -void FramelessQuickHelper::addIgnoreArea(const QRect &val) -{ - FramelessWindowsManager::addIgnoreArea(window(), val); -} - -void FramelessQuickHelper::addDraggableArea(const QRect &val) -{ - FramelessWindowsManager::addDraggableArea(window(), val); + FramelessWindowsManager::addWindow(window()); } void FramelessQuickHelper::addIgnoreObject(QQuickItem *val) @@ -193,12 +144,6 @@ void FramelessQuickHelper::addIgnoreObject(QQuickItem *val) FramelessWindowsManager::addIgnoreObject(window(), val); } -void FramelessQuickHelper::addDraggableObject(QQuickItem *val) -{ - Q_ASSERT(val); - FramelessWindowsManager::addDraggableObject(window(), val); -} - #ifdef Q_OS_WINDOWS void FramelessQuickHelper::timerEvent(QTimerEvent *event) { @@ -216,18 +161,11 @@ void FramelessQuickHelper::setWindowFrameVisible(const bool value) { if (value) { qputenv(g_sPreserveWindowFrame, "1"); - qputenv(g_sDontExtendFrame, "1"); } else { qunsetenv(g_sPreserveWindowFrame); - qunsetenv(g_sDontExtendFrame); } } -void FramelessQuickHelper::displaySystemMenu(const QPointF &pos) -{ - WinNativeEventFilter::displaySystemMenu(window(), pos); -} - void FramelessQuickHelper::setBlurEffectEnabled(const bool enabled, const bool forceAcrylic, const QColor &gradientColor) diff --git a/framelessquickhelper.h b/framelessquickhelper.h index c8775a7..e3cab7f 100644 --- a/framelessquickhelper.h +++ b/framelessquickhelper.h @@ -53,10 +53,6 @@ class FramelessQuickHelper : public QQuickItem Q_PROPERTY( int titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged) Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged) - Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize NOTIFY minimumSizeChanged) - Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize NOTIFY maximumSizeChanged) - Q_PROPERTY(bool titleBarEnabled READ titleBarEnabled WRITE setTitleBarEnabled NOTIFY - titleBarEnabledChanged) #ifdef Q_OS_WINDOWS Q_PROPERTY(bool canHaveWindowFrame READ canHaveWindowFrame CONSTANT) Q_PROPERTY(bool colorizationEnabled READ colorizationEnabled NOTIFY colorizationEnabledChanged) @@ -86,15 +82,6 @@ public: bool resizable() const; void setResizable(const bool val); - QSize minimumSize() const; - void setMinimumSize(const QSize &val); - - QSize maximumSize() const; - void setMaximumSize(const QSize &val); - - bool titleBarEnabled() const; - void setTitleBarEnabled(const bool val); - #ifdef Q_OS_WINDOWS bool canHaveWindowFrame() const; bool colorizationEnabled() const; @@ -107,19 +94,12 @@ public: #endif public Q_SLOTS: - void removeWindowFrame(const bool center = false); - - void moveWindowToDesktopCenter(); - - void addIgnoreArea(const QRect &val); - void addDraggableArea(const QRect &val); + void removeWindowFrame(); void addIgnoreObject(QQuickItem *val); - void addDraggableObject(QQuickItem *val); #ifdef Q_OS_WINDOWS void setWindowFrameVisible(const bool value = true); - void displaySystemMenu(const QPointF &pos = {}); void setBlurEffectEnabled(const bool enabled = true, const bool forceAcrylic = false, const QColor &gradientColor = Qt::white); @@ -135,9 +115,6 @@ Q_SIGNALS: void borderHeightChanged(int); void titleBarHeightChanged(int); void resizableChanged(bool); - void minimumSizeChanged(const QSize &); - void maximumSizeChanged(const QSize &); - void titleBarEnabledChanged(bool); #ifdef Q_OS_WINDOWS void colorizationEnabledChanged(bool); void colorizationColorChanged(const QColor &); diff --git a/framelesswindowsmanager.cpp b/framelesswindowsmanager.cpp index f9c0d04..d73f26e 100644 --- a/framelesswindowsmanager.cpp +++ b/framelesswindowsmanager.cpp @@ -38,52 +38,13 @@ Q_GLOBAL_STATIC(FramelessHelper, framelessHelper) FramelessWindowsManager::FramelessWindowsManager() = default; -void FramelessWindowsManager::addWindow(const QWindow *window, const bool center) +void FramelessWindowsManager::addWindow(const QWindow *window) { Q_ASSERT(window); #ifdef Q_OS_WINDOWS - WinNativeEventFilter::addFramelessWindow(window); + WinNativeEventFilter::addFramelessWindow(const_cast(window)); #else framelessHelper()->removeWindowFrame(window); -#endif - if (center) { - moveWindowToDesktopCenter(window); - } -} - -void FramelessWindowsManager::moveWindowToDesktopCenter(const QWindow *window) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - WinNativeEventFilter::moveWindowToDesktopCenter(window); -#else - FramelessHelper::moveWindowToDesktopCenter(window); -#endif -} - -void FramelessWindowsManager::addIgnoreArea(const QWindow *window, const QRect &area) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->ignoreAreas.append(area); - } -#else - framelessHelper()->addIgnoreArea(window, area); -#endif -} - -void FramelessWindowsManager::addDraggableArea(const QWindow *window, const QRect &area) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->draggableAreas.append(area); - } -#else - framelessHelper()->addDraggableArea(window, area); #endif } @@ -91,34 +52,21 @@ void FramelessWindowsManager::addIgnoreObject(const QWindow *window, QObject *ob { Q_ASSERT(window); #ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->ignoreObjects.append(object); - } + QObjectList objects = WinNativeEventFilter::getIgnoredObjects(window); + objects.append(object); + WinNativeEventFilter::setIgnoredObjects(const_cast(window), objects); #else framelessHelper()->addIgnoreObject(window, object); #endif } -void FramelessWindowsManager::addDraggableObject(const QWindow *window, QObject *object) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->draggableObjects.append(object); - } -#else - framelessHelper()->addDraggableObject(window, object); -#endif -} - int FramelessWindowsManager::getBorderWidth(const QWindow *window) { #ifdef Q_OS_WINDOWS Q_ASSERT(window); return WinNativeEventFilter::getSystemMetric(window, - WinNativeEventFilter::SystemMetric::BorderWidth); + WinNativeEventFilter::SystemMetric::BorderWidth, + false); #else Q_UNUSED(window) return framelessHelper()->getBorderWidth(); @@ -129,10 +77,7 @@ void FramelessWindowsManager::setBorderWidth(const QWindow *window, const int va { #ifdef Q_OS_WINDOWS Q_ASSERT(window); - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->borderWidth = value; - } + WinNativeEventFilter::setBorderWidth(const_cast(window), value); #else Q_UNUSED(window) framelessHelper()->setBorderWidth(value); @@ -144,7 +89,8 @@ int FramelessWindowsManager::getBorderHeight(const QWindow *window) #ifdef Q_OS_WINDOWS Q_ASSERT(window); return WinNativeEventFilter::getSystemMetric(window, - WinNativeEventFilter::SystemMetric::BorderHeight); + WinNativeEventFilter::SystemMetric::BorderHeight, + false); #else Q_UNUSED(window) return framelessHelper()->getBorderHeight(); @@ -155,10 +101,7 @@ void FramelessWindowsManager::setBorderHeight(const QWindow *window, const int v { #ifdef Q_OS_WINDOWS Q_ASSERT(window); - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->borderHeight = value; - } + WinNativeEventFilter::setBorderHeight(const_cast(window), value); #else Q_UNUSED(window) framelessHelper()->setBorderHeight(value); @@ -170,7 +113,8 @@ int FramelessWindowsManager::getTitleBarHeight(const QWindow *window) #ifdef Q_OS_WINDOWS Q_ASSERT(window); return WinNativeEventFilter::getSystemMetric(window, - WinNativeEventFilter::SystemMetric::TitleBarHeight); + WinNativeEventFilter::SystemMetric::TitleBarHeight, + false); #else Q_UNUSED(window) return framelessHelper()->getTitleBarHeight(); @@ -181,10 +125,7 @@ void FramelessWindowsManager::setTitleBarHeight(const QWindow *window, const int { #ifdef Q_OS_WINDOWS Q_ASSERT(window); - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->titleBarHeight = value; - } + WinNativeEventFilter::setTitleBarHeight(const_cast(window), value); #else Q_UNUSED(window) framelessHelper()->setTitleBarHeight(value); @@ -195,8 +136,15 @@ bool FramelessWindowsManager::getResizable(const QWindow *window) { Q_ASSERT(window); #ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - return data ? !data->fixedSize : true; + if (window->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint)) { + return false; + } + const QSize minSize = window->minimumSize(); + const QSize maxSize = window->maximumSize(); + if (!minSize.isEmpty() && !maxSize.isEmpty() && minSize == maxSize) { + return false; + } + return true; #else return framelessHelper()->getResizable(window); #endif @@ -206,80 +154,8 @@ void FramelessWindowsManager::setResizable(const QWindow *window, const bool val { Q_ASSERT(window); #ifdef Q_OS_WINDOWS - WinNativeEventFilter::setWindowResizable(window, value); + const_cast(window)->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value); #else framelessHelper()->setResizable(window, value); #endif } - -QSize FramelessWindowsManager::getMinimumSize(const QWindow *window) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - return data ? data->minimumSize : QSize(); -#else - return window->minimumSize(); -#endif -} - -void FramelessWindowsManager::setMinimumSize(const QWindow *window, const QSize &value) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->minimumSize = value; - } -#else - window->setMinimumSize(value); -#endif -} - -QSize FramelessWindowsManager::getMaximumSize(const QWindow *window) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - return data ? data->maximumSize : QSize(); -#else - return window->maximumSize(); -#endif -} - -void FramelessWindowsManager::setMaximumSize(const QWindow *window, const QSize &value) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->maximumSize = value; - } -#else - window->setMaximumSize(value); -#endif -} - -bool FramelessWindowsManager::getTitleBarEnabled(const QWindow *window) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - return data ? !data->disableTitleBar : true; -#else - return framelessHelper()->getTitleBarEnabled(window); -#endif -} - -void FramelessWindowsManager::setTitleBarEnabled(const QWindow *window, const bool value) -{ - Q_ASSERT(window); -#ifdef Q_OS_WINDOWS - const auto data = WinNativeEventFilter::getWindowData(window); - if (data) { - data->disableTitleBar = !value; - } -#else - framelessHelper()->setTitleBarEnabled(window, value); -#endif -} diff --git a/framelesswindowsmanager.h b/framelesswindowsmanager.h index 5558bef..9608c1f 100644 --- a/framelesswindowsmanager.h +++ b/framelesswindowsmanager.h @@ -55,15 +55,9 @@ public: explicit FramelessWindowsManager(); ~FramelessWindowsManager() = default; - static void addWindow(const QWindow *window, const bool center = false); - - static void moveWindowToDesktopCenter(const QWindow *window); - - static void addIgnoreArea(const QWindow *window, const QRect &area); - static void addDraggableArea(const QWindow *window, const QRect &area); + static void addWindow(const QWindow *window); static void addIgnoreObject(const QWindow *window, QObject *object); - static void addDraggableObject(const QWindow *window, QObject *object); static int getBorderWidth(const QWindow *window); static void setBorderWidth(const QWindow *window, const int value); @@ -76,13 +70,4 @@ public: static bool getResizable(const QWindow *window); static void setResizable(const QWindow *window, const bool value = true); - - static QSize getMinimumSize(const QWindow *window); - static void setMinimumSize(const QWindow *window, const QSize &value); - - static QSize getMaximumSize(const QWindow *window); - static void setMaximumSize(const QWindow *window, const QSize &value); - - static bool getTitleBarEnabled(const QWindow *window); - static void setTitleBarEnabled(const QWindow *window, const bool value = true); }; diff --git a/winnativeeventfilter.cpp b/winnativeeventfilter.cpp index b45c3f2..68fa5e8 100644 --- a/winnativeeventfilter.cpp +++ b/winnativeeventfilter.cpp @@ -36,8 +36,6 @@ #include #include #include -#include -#include #include #include #include @@ -690,7 +688,6 @@ using WNEF_CORE_DATA = struct _WNEF_CORE_DATA #endif // WNEF_LINK_SYSLIB - int m_borderWidth = -1, m_borderHeight = -1, m_titleBarHeight = -1; QScopedPointer m_instance; }; @@ -700,7 +697,7 @@ Q_GLOBAL_STATIC(WNEF_CORE_DATA, coreData) namespace { -const UINT m_defaultDotsPerInch = USER_DEFAULT_SCREEN_DPI; +const quint32 m_defaultDotsPerInch = USER_DEFAULT_SCREEN_DPI; const qreal m_defaultDevicePixelRatio = 1.0; @@ -708,7 +705,6 @@ const char envVarUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR"; const char envVarPreserveWindowFrame[] = "WNEF_PRESERVE_WINDOW_FRAME"; const char envVarForceWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME"; const char envVarForceAcrylic[] = "WNEF_FORCE_ACRYLIC_ON_WIN10"; -const char envVarNoExtendFrame[] = "WNEF_DO_NOT_EXTEND_FRAME"; bool shouldUseNativeTitleBar() { @@ -742,12 +738,7 @@ bool forceEnableAcrylicOnWin10() return qEnvironmentVariableIsSet(envVarForceAcrylic); } -bool dontExtendFrame() -{ - return qEnvironmentVariableIsSet(envVarNoExtendFrame); -} - -BOOL IsDwmCompositionEnabled() +bool isDwmCompositionEnabled() { // Since Win8, DWM composition is always enabled and can't be disabled. // In other words, DwmIsCompositionEnabled will always return TRUE on @@ -757,414 +748,45 @@ BOOL IsDwmCompositionEnabled() && enabled; } -WINDOWINFO GetInfoForWindow(const HWND handle) +QWindow *findWindow(const HWND handle) { Q_ASSERT(handle); - WINDOWINFO windowInfo; - SecureZeroMemory(&windowInfo, sizeof(windowInfo)); - windowInfo.cbSize = sizeof(windowInfo); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - WNEF_EXECUTE_WINAPI(GetWindowInfo, handle, &windowInfo) - } - return windowInfo; -} - -MONITORINFO GetMonitorInfoForWindow(const HWND handle) -{ - Q_ASSERT(handle); - MONITORINFO monitorInfo; - SecureZeroMemory(&monitorInfo, sizeof(monitorInfo)); - monitorInfo.cbSize = sizeof(monitorInfo); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - const HMONITOR monitor = WNEF_EXECUTE_WINAPI_RETURN(MonitorFromWindow, - nullptr, - handle, - MONITOR_DEFAULTTONEAREST); - if (monitor) { - WNEF_EXECUTE_WINAPI(GetMonitorInfoW, monitor, &monitorInfo) - } - } - return monitorInfo; -} - -BOOL IsFullScreen(const HWND handle) -{ - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - const WINDOWINFO windowInfo = GetInfoForWindow(handle); - const MONITORINFO monitorInfo = GetMonitorInfoForWindow(handle); - // The only way to judge whether a window is fullscreen or not - // is to compare it's size with the screen's size, there is no official - // Win32 API to do this for us. - return WNEF_EXECUTE_WINAPI_RETURN(EqualRect, - FALSE, - &windowInfo.rcWindow, - &monitorInfo.rcMonitor) - || WNEF_EXECUTE_WINAPI_RETURN(EqualRect, - FALSE, - &windowInfo.rcClient, - &monitorInfo.rcMonitor); - } - return FALSE; -} - -[[maybe_unused]] BOOL IsTopLevel(const HWND handle) -{ - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - if (WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, handle, GWL_STYLE) & WS_CHILD) { - return FALSE; - } - const HWND parent = WNEF_EXECUTE_WINAPI_RETURN(GetAncestor, nullptr, handle, GA_PARENT); - if (parent && (parent != WNEF_EXECUTE_WINAPI_RETURN(GetDesktopWindow, nullptr))) { - return FALSE; - } - return TRUE; - } - return FALSE; -} - -BOOL IsApplicationDpiAware() -{ - if (coreData()->m_lpGetProcessDpiAwareness) { - PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE; - coreData()->m_lpGetProcessDpiAwareness(WNEF_EXECUTE_WINAPI_RETURN(GetCurrentProcess, - nullptr), - &awareness); - return (awareness != PROCESS_DPI_UNAWARE); - } else { - return WNEF_EXECUTE_WINAPI_RETURN(IsProcessDPIAware, FALSE); - } -} - -UINT GetDotsPerInchForSystem() -{ - const auto getScreenDpi = [](const UINT defaultValue) -> UINT { - // Using Direct2D to get the screen DPI. - // Available since Windows 7. - ID2D1Factory *m_pDirect2dFactory = nullptr; - if (SUCCEEDED(WNEF_EXECUTE_WINAPI_RETURN(D2D1CreateFactory, - E_FAIL, - D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory), - nullptr, - reinterpret_cast(&m_pDirect2dFactory))) - && m_pDirect2dFactory) { - m_pDirect2dFactory->ReloadSystemMetrics(); - FLOAT dpiX = defaultValue, dpiY = defaultValue; - m_pDirect2dFactory->GetDesktopDpi(&dpiX, &dpiY); - // The values of *dpiX and *dpiY are identical. - return qRound(dpiX == dpiY ? dpiY : dpiX); - } - // Available since Windows 2000. - const HDC hdc = WNEF_EXECUTE_WINAPI_RETURN(GetDC, nullptr, nullptr); - if (hdc) { - const int dpiX = WNEF_EXECUTE_WINAPI_RETURN(GetDeviceCaps, 0, hdc, LOGPIXELSX); - const int dpiY = WNEF_EXECUTE_WINAPI_RETURN(GetDeviceCaps, 0, hdc, LOGPIXELSY); - WNEF_EXECUTE_WINAPI(ReleaseDC, nullptr, hdc) - // The values of dpiX and dpiY are identical actually, just to - // silence a compiler warning. - return dpiX == dpiY ? dpiY : dpiX; - } - return defaultValue; - }; - if (coreData()->m_lpGetSystemDpiForProcess) { - return coreData()->m_lpGetSystemDpiForProcess( - WNEF_EXECUTE_WINAPI_RETURN(GetCurrentProcess, nullptr)); - } - if (coreData()->m_lpGetDpiForSystem) { - return coreData()->m_lpGetDpiForSystem(); - } - return getScreenDpi(m_defaultDotsPerInch); -} - -UINT GetDotsPerInchForWindow(const HWND handle) -{ - Q_ASSERT(handle); - if (!IsApplicationDpiAware()) { - // Return hard-coded DPI if DPI scaling is disabled. - return m_defaultDotsPerInch; - } - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - if (coreData()->m_lpGetDpiForWindow) { - return coreData()->m_lpGetDpiForWindow(handle); - } - if (coreData()->m_lpGetDpiForMonitor) { - UINT dpiX = m_defaultDotsPerInch, dpiY = m_defaultDotsPerInch; - coreData()->m_lpGetDpiForMonitor(WNEF_EXECUTE_WINAPI_RETURN(MonitorFromWindow, - nullptr, - handle, - MONITOR_DEFAULTTONEAREST), - MDT_EFFECTIVE_DPI, - &dpiX, - &dpiY); - // The values of *dpiX and *dpiY are identical. - return dpiX == dpiY ? dpiY : dpiX; - } - } - return GetDotsPerInchForSystem(); -} - -qreal GetPreferedNumber(const qreal num) -{ - qreal result = -1.0; - const auto getRoundedNumber = [](const qreal in) -> qreal { - // If the given number is not very large, we assume it's a - // device pixel ratio (DPR), otherwise we assume it's a DPI. - if (in < m_defaultDotsPerInch) { - return qRound(in); - } else { - if (in < (m_defaultDotsPerInch * 1.5)) { - return m_defaultDotsPerInch; - } else if (in < (m_defaultDotsPerInch * 2.5)) { - return m_defaultDotsPerInch * 2; - } else if (in < (m_defaultDotsPerInch * 3.5)) { - return m_defaultDotsPerInch * 3; - } else if (in < (m_defaultDotsPerInch * 4.5)) { - return m_defaultDotsPerInch * 4; - } else if (in < (m_defaultDotsPerInch * 5.5)) { - return m_defaultDotsPerInch * 5; - } else { - qWarning().noquote() << "DPI too large:" << static_cast(in); - } - } - return -1.0; - }; -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - switch (QGuiApplication::highDpiScaleFactorRoundingPolicy()) { - case Qt::HighDpiScaleFactorRoundingPolicy::PassThrough: - // Default behavior for Qt 6. - result = num; - break; - case Qt::HighDpiScaleFactorRoundingPolicy::Floor: - result = qFloor(num); - break; - case Qt::HighDpiScaleFactorRoundingPolicy::Ceil: - result = qCeil(num); - break; - default: - // Default behavior for Qt 5.6 to 5.15 - result = getRoundedNumber(num); - break; - } -#else - // Default behavior for Qt 5.6 to 5.15 - result = getRoundedNumber(num); -#endif - return result; -} - -qreal GetDevicePixelRatioForWindow(const HWND handle) -{ - Q_ASSERT(handle); - qreal result = m_defaultDevicePixelRatio; - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - result = static_cast(GetDotsPerInchForWindow(handle)) - / static_cast(m_defaultDotsPerInch); - } - return GetPreferedNumber(result); -} - -[[maybe_unused]] RECT GetFrameSizeForWindow(const HWND handle, const BOOL includingTitleBar = FALSE) -{ - Q_ASSERT(handle); - RECT rect = {0, 0, 0, 0}; - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - const auto style = WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, handle, GWL_STYLE); - // It's the same with using GetSystemMetrics, the returned values - // of the two functions are identical. - if (coreData()->m_lpAdjustWindowRectExForDpi) { - coreData()->m_lpAdjustWindowRectExForDpi(&rect, - includingTitleBar ? (style | WS_CAPTION) - : (style & ~WS_CAPTION), - FALSE, - WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, - 0, - handle, - GWL_EXSTYLE), - GetDotsPerInchForWindow(handle)); - } else { - WNEF_EXECUTE_WINAPI(AdjustWindowRectEx, - &rect, - includingTitleBar ? (style | WS_CAPTION) : (style & ~WS_CAPTION), - FALSE, - WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, handle, GWL_EXSTYLE)) - const qreal dpr = GetDevicePixelRatioForWindow(handle); - rect.top = qRound(rect.top * dpr); - rect.bottom = qRound(rect.bottom * dpr); - rect.left = qRound(rect.left * dpr); - rect.right = qRound(rect.right * dpr); - } - // Some values may be negative. Make them positive unconditionally. - rect.top = qAbs(rect.top); - rect.bottom = qAbs(rect.bottom); - rect.left = qAbs(rect.left); - rect.right = qAbs(rect.right); - } - return rect; -} - -void UpdateFrameMarginsForWindow(const HWND handle, const bool resetToDefault = false) -{ - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - MARGINS margins = {0, 0, 0, 0}; - // The frame shadow is drawn on the non-client area and thus we have - // to make sure the non-client area rendering is enabled first. - const DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED; - WNEF_EXECUTE_WINAPI(DwmSetWindowAttribute, - handle, - DWMWA_NCRENDERING_POLICY, - &ncrp, - sizeof(ncrp)) - // Use negative values have the same effect, however, it will - // cause the window become transparent when it's maximizing or - // restoring from maximized. Just like flashing. Fixing it by - // passing positive values. - // The system won't draw the frame shadow if the window doesn't - // have a frame, so we have to extend the frame a bit to let the - // system draw the shadow. We won't see any frame even we have - // extended it because we have turned the whole window area into - // the client area in WM_NCCALCSIZE so we won't see it due to - // it's covered by the client area (in other words, it's still - // there, we just can't see it). - if (IsDwmCompositionEnabled() && !IsMaximized(handle) && !IsFullScreen(handle)) { - margins.cyTopHeight = 1; - } - if (resetToDefault || shouldUseNativeTitleBar() || dontExtendFrame()) { - // If we are going to use the native title bar, - // we should use the original window frame as well. - margins = {0, 0, 0, 0}; - } - WNEF_EXECUTE_WINAPI(DwmExtendFrameIntoClientArea, handle, &margins) - } -} - -int GetSystemMetricsForWindow(const HWND handle, const int index, const bool dpiAware = false) -{ - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - if (coreData()->m_lpGetSystemMetricsForDpi) { - const UINT dpi = dpiAware ? qRound(GetPreferedNumber(GetDotsPerInchForWindow(handle))) - : m_defaultDotsPerInch; - return coreData()->m_lpGetSystemMetricsForDpi(index, dpi); - } else { - // Although Microsoft claims that GetSystemMetrics() is not DPI - // aware, it still returns a scaled value on Win7, Win8.1 and - // Win10. - const qreal dpr = dpiAware ? 1.0 : GetDevicePixelRatioForWindow(handle); - return qRound(WNEF_EXECUTE_WINAPI_RETURN(GetSystemMetrics, 0, index) / dpr); - } - } - return 0; -} - -QWindow *getWindowFromRawHandle(const HWND handle) -{ - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - const auto wid = reinterpret_cast(handle); - const auto windows = QGuiApplication::topLevelWindows(); - for (auto &&window : qAsConst(windows)) { - if (window && window->handle()) { - if (window->winId() == wid) { - return window; - } + const auto wid = reinterpret_cast(handle); + const QWindowList windows = QGuiApplication::topLevelWindows(); + for (auto &&window : qAsConst(windows)) { + if (window && window->handle()) { + if (window->winId() == wid) { + return window; } } } return nullptr; } -HWND getRawHandleFromWindow(const QWindow *window) +void triggerFrameChange(const QWindow *window) { Q_ASSERT(window); - const auto handle = window->handle(); - return handle ? reinterpret_cast(handle->winId()) : nullptr; + WNEF_EXECUTE_WINAPI(SetWindowPos, + reinterpret_cast(window->winId()), + nullptr, + 0, + 0, + 0, + 0, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER + | SWP_NOOWNERZORDER) } -void createUserData(const HWND handle, const WinNativeEventFilter::WINDOWDATA *data = nullptr) +void updateFrameMargins(const QWindow *window, bool reset) { - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - const auto userData = reinterpret_cast( - WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, handle, GWLP_USERDATA)); - if (userData) { - if (data) { - *userData = *data; - } - } else { - // Yes, this is a memory leak, but it doesn't hurt much, unless your - // application has thousands of windows. - auto *_data = new WinNativeEventFilter::WINDOWDATA; - if (data) { - *_data = *data; - } - WNEF_EXECUTE_WINAPI(SetWindowLongPtrW, - handle, - GWLP_USERDATA, - reinterpret_cast(_data)) - WinNativeEventFilter::updateWindow(getWindowFromRawHandle(handle), true, false); - } - } -} - -void qCoreAppFixup() -{ - if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) { - QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); - } -} - -void updateQtFrame_internal(const HWND handle, const bool resetToDefault = false) -{ - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - QWindow *window = getWindowFromRawHandle(handle); - if (window) { - const int tbh = resetToDefault ? 0 - : WinNativeEventFilter::getSystemMetric( - window, - WinNativeEventFilter::SystemMetric::TitleBarHeight, - true, - true); - WinNativeEventFilter::updateQtFrame(window, tbh); - } - } -} - -QString getCurrentScreenIdentifier(const HWND handle) -{ - Q_ASSERT(handle); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { - QScreen *currentScreen = nullptr; - const QWindow *window = getWindowFromRawHandle(handle); - if (window) { - currentScreen = window->screen(); - } - if (currentScreen) { - const QString sn = currentScreen->serialNumber().toUpper(); - return sn.isEmpty() ? currentScreen->name().toUpper() : sn; - } - } - return {}; -} - -void install() -{ - qCoreAppFixup(); - if (coreData()->m_instance.isNull()) { - coreData()->m_instance.reset(new WinNativeEventFilter); - qApp->installNativeEventFilter(coreData()->m_instance.data()); - } -} - -[[maybe_unused]] void uninstall() -{ - if (!coreData()->m_instance.isNull()) { - qApp->removeNativeEventFilter(coreData()->m_instance.data()); - coreData()->m_instance.reset(); + Q_ASSERT(window); + MARGINS margins = {0, 0, 0, 0}; + if (!reset) { + margins.cyTopHeight = 1; } + WNEF_EXECUTE_WINAPI(DwmExtendFrameIntoClientArea, + reinterpret_cast(window->winId()), + &margins) } // The standard values of border width, border height and title bar height @@ -1179,59 +801,96 @@ const QLatin1String g_sDwmRegistryKey(R"(HKEY_CURRENT_USER\Software\Microsoft\Wi const QLatin1String g_sPersonalizeRegistryKey( R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"); +const char m_framelessMode[] = "_WNEF_FRAMELESS_MODE_ENABLED"; +const char m_borderWidth[] = "_WNEF_WINDOW_BORDER_WIDTH"; +const char m_borderHeight[] = "_WNEF_WINDOW_BORDER_HEIGHT"; +const char m_titleBarHeight[] = "_WNEF_TITLE_BAR_HEIGHT"; +const char m_ignoredObjects[] = "_WNEF_TITLE_BAR_IGNORED_OBJECTS"; + +void setup() +{ + if (coreData()->m_instance.isNull()) { + coreData()->m_instance.reset(new WinNativeEventFilter); + qApp->installNativeEventFilter(coreData()->m_instance.get()); + } +} + } // namespace -WinNativeEventFilter::WinNativeEventFilter() -{ - qCoreAppFixup(); -} +WinNativeEventFilter::WinNativeEventFilter() = default; -void WinNativeEventFilter::addFramelessWindow(const QWindow *window, - const WINDOWDATA *data, - const bool center, - const int x, - const int y, - const int width, - const int height) +WinNativeEventFilter::~WinNativeEventFilter() { - Q_ASSERT(window); - qCoreAppFixup(); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - createUserData(hwnd); - const auto oldData = getWindowData(window); - if (oldData && oldData->framelessModeEnabled) { - return; - } - const auto newData = new WINDOWDATA; - if (data) { - *newData = *data; - } - newData->framelessModeEnabled = true; - createUserData(hwnd, newData); - install(); - updateQtFrame_internal(hwnd); - if ((x > 0) && (y > 0) && (width > 0) && (height > 0)) { - setWindowGeometry(window, x, y, width, height); - } - if (center) { - moveWindowToDesktopCenter(window); - } + if (!coreData()->m_instance.isNull()) { + qApp->removeNativeEventFilter(coreData()->m_instance.get()); } } -void WinNativeEventFilter::removeFramelessWindow(const QWindow *window) +void WinNativeEventFilter::addFramelessWindow(QWindow *window) { Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - createUserData(hwnd); - const auto data = getWindowData(window); - if (data) { - data->framelessModeEnabled = false; + setup(); + window->setProperty(m_framelessMode, true); + const QMargins margins = {0, + -getSystemMetric(window, SystemMetric::TitleBarHeight, true, true), + 0, + 0}; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QPlatformWindow *platformWindow = window->handle(); + if (platformWindow) { + QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow, + QString::fromUtf8( + "WindowsCustomMargins"), + QVariant::fromValue(margins)); } - updateQtFrame_internal(hwnd, true); - UpdateFrameMarginsForWindow(hwnd, true); - updateWindow(window, true, false); +#else + auto *platformWindow = dynamic_cast( + window->handle()); + if (platformWindow) { + platformWindow->setCustomMargins(margins); + } +#endif + updateFrameMargins(window, false); + triggerFrameChange(window); +} + +bool WinNativeEventFilter::isWindowFrameless(const QWindow *window) +{ + Q_ASSERT(window); + return window->property(m_framelessMode).toBool(); +} + +void WinNativeEventFilter::removeFramelessWindow(QWindow *window) +{ + Q_ASSERT(window); + window->setProperty(m_framelessMode, false); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QPlatformWindow *platformWindow = window->handle(); + if (platformWindow) { + QGuiApplication::platformNativeInterface() + ->setWindowProperty(platformWindow, QString::fromUtf8("WindowsCustomMargins"), {}); + } +#else + auto *platformWindow = dynamic_cast( + window->handle()); + if (platformWindow) { + platformWindow->setCustomMargins({}); + } +#endif + updateFrameMargins(window, true); + triggerFrameChange(window); +} + +void WinNativeEventFilter::setIgnoredObjects(QWindow *window, const QObjectList &objects) +{ + Q_ASSERT(window); + window->setProperty(m_ignoredObjects, QVariant::fromValue(objects)); +} + +QObjectList WinNativeEventFilter::getIgnoredObjects(const QWindow *window) +{ + Q_ASSERT(window); + return qvariant_cast(window->property(m_ignoredObjects)); } #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -1269,98 +928,10 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, // Anyway, we should skip it in this case. return false; } - const QWindow *window = getWindowFromRawHandle(msg->hwnd); - if (!window) { + const QWindow *window = findWindow(msg->hwnd); + if (!window || (window && !window->property(m_framelessMode).toBool())) { return false; } - const auto data = reinterpret_cast( - WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, msg->hwnd, GWLP_USERDATA)); - if (!data) { - // Work-around a long existing Windows bug. - // Overlapped windows will receive a WM_GETMINMAXINFO message before - // WM_NCCREATE. This is safe to ignore. It doesn't need any special - // handling anyway. - if (msg->message == WM_NCCREATE) { - const auto userData = reinterpret_cast(msg->lParam)->lpCreateParams; - WNEF_EXECUTE_WINAPI(SetWindowLongPtrW, - msg->hwnd, - GWLP_USERDATA, - reinterpret_cast(userData)) - // Copied from MSDN without any modification: - // If you have changed certain window data using SetWindowLong, - // you must call SetWindowPos for the changes to take effect. - // Use the following combination for uFlags: SWP_NOMOVE | - // SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED. - updateWindow(window, true, false); - } - *result = WNEF_EXECUTE_WINAPI_RETURN(DefWindowProcW, - 0, - msg->hwnd, - msg->message, - msg->wParam, - msg->lParam); - return false; - } - if (!data->framelessModeEnabled) { - return false; - } - if (!data->initialized) { - // Avoid initializing a same window twice. - data->initialized = true; - // Record the current screen. - data->currentScreen = getCurrentScreenIdentifier(msg->hwnd); - Q_ASSERT(!data->currentScreen.isEmpty()); - // Don't restore the window styles to default when you are - // developing Qt Quick applications because the QWindow - // will disappear once you do it. However, Qt Widgets applications - // are not affected. Don't know why currently. - if (data->restoreDefaultWindowStyle) { - // Restore default window style. - // WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU - // | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX - // Apply the WS_OVERLAPPEDWINDOW window style to restore the - // window to a normal native Win32 window. - // Don't apply the Qt::FramelessWindowHint flag, it will add - // the WS_POPUP window style to the window, which will turn - // the window into a popup window, losing all the functions - // a normal window should have. - // WS_CLIPCHILDREN | WS_CLIPSIBLINGS: work-around strange bugs. - WNEF_EXECUTE_WINAPI(SetWindowLongPtrW, - msg->hwnd, - GWL_STYLE, - WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS) - updateWindow(window, true, false); - } - if (data->enableLayeredWindow) { - // Turn our window into a layered window to get better - // performance and hopefully, to get rid of some strange bugs at - // the same time. But this will break the Arcylic effect - // (introduced in Win10 1709), if you use the undocumented API - // SetWindowCompositionAttribute to enable it for this window, - // the whole window will become totally black. Don't know why - // currently. - WNEF_EXECUTE_WINAPI(SetWindowLongPtrW, - msg->hwnd, - GWL_EXSTYLE, - WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, - 0, - msg->hwnd, - GWL_EXSTYLE) - | WS_EX_LAYERED) - updateWindow(window, true, false); - // A layered window can't be visible unless we call - // SetLayeredWindowAttributes or UpdateLayeredWindow once. - WNEF_EXECUTE_WINAPI(SetLayeredWindowAttributes, - msg->hwnd, - RGB(255, 0, 255), - 0, - LWA_COLORKEY) - } - // Bring our frame shadow back through DWM, don't draw it manually. - UpdateFrameMarginsForWindow(msg->hwnd); - // Blur effect. - setBlurEffectEnabled(window, data->enableBlurBehindWindow); - } switch (msg->message) { case WM_NCCALCSIZE: { // Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容) @@ -1437,16 +1008,17 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, break; } - const auto mode = static_cast(msg->wParam); + if (!msg->wParam) { + *result = 0; + return true; + } // If the window bounds change, we're going to relayout and repaint // anyway. Returning WVR_REDRAW avoids an extra paint before that of // the old client pixels in the (now wrong) location, and thus makes // actions like resizing a window from the left edge look slightly // less broken. - *result = mode ? WVR_REDRAW : 0; - const auto clientRect = mode - ? &(reinterpret_cast(msg->lParam)->rgrc[0]) - : reinterpret_cast(msg->lParam); + *result = WVR_REDRAW; + const auto clientRect = &(reinterpret_cast(msg->lParam)->rgrc[0]); if (shouldHaveWindowFrame()) { // Store the original top before the default window proc // applies the default frame. @@ -1469,7 +1041,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, // We don't need this correction when we're fullscreen. We will // have the WS_POPUP size, so we don't have to worry about // borders, and the default frame will be fine. - if (IsMaximized(msg->hwnd) && !IsFullScreen(msg->hwnd)) { + if (IsMaximized(msg->hwnd) && !(window->windowState() & Qt::WindowFullScreen)) { // Windows automatically adds a standard width border to all // sides when a window is maximized. We have to remove it // otherwise the content of our window will be cut-off from @@ -1507,7 +1079,14 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, // we have to use another way to judge this if we are // running on Windows 7 or Windows 8. if (isWin8Point1OrGreater()) { - const MONITORINFO monitorInfo = GetMonitorInfoForWindow(msg->hwnd); + MONITORINFO monitorInfo; + SecureZeroMemory(&monitorInfo, sizeof(monitorInfo)); + monitorInfo.cbSize = sizeof(monitorInfo); + const HMONITOR monitor = WNEF_EXECUTE_WINAPI_RETURN(MonitorFromWindow, + nullptr, + msg->hwnd, + MONITOR_DEFAULTTONEAREST); + WNEF_EXECUTE_WINAPI(GetMonitorInfoW, monitor, &monitorInfo) // This helper can be used to determine if there's a // auto-hide taskbar on the given edge of the monitor // we're currently on. @@ -1587,17 +1166,6 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, // area. *result = 0; } - /* - // It does solve the flickering issue indeed, however, it also - // causes a lot of new issues when we are trying to draw - // something on the window manually through QPainter. - if (!shouldHaveWindowFrame() && !IsFullScreen(msg->hwnd) && !IsMaximized(msg->hwnd)) { - // Fix the flickering problem when resizing. - // Don't modify the left, right or bottom edge because - // a border line will be seen (at least on Win10). - clientRect->top -= 1; - } - */ return true; } // These undocumented messages are sent to draw themed window @@ -1615,7 +1183,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, case WM_NCPAINT: { // 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失 - if (!IsDwmCompositionEnabled() && !shouldHaveWindowFrame()) { + if (!isDwmCompositionEnabled() && !shouldHaveWindowFrame()) { // Only block WM_NCPAINT when DWM composition is disabled. If // it's blocked when DWM composition is enabled, the frame // shadow won't be drawn. @@ -1629,7 +1197,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, if (shouldHaveWindowFrame()) { break; } else { - if (IsDwmCompositionEnabled()) { + if (isDwmCompositionEnabled()) { // DefWindowProc won't repaint the window border if lParam // (normally a HRGN) is -1. See the following link's "lParam" // section: @@ -1722,28 +1290,6 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, break; } - if (data->mouseTransparent) { - // Mouse events will be passed to the parent window. - *result = HTTRANSPARENT; - return true; - } - - const auto isInSpecificAreas = - [](const QPointF &mousePos, const QList &areas, const qreal dpr) -> bool { - if (areas.isEmpty()) { - return false; - } - for (auto &&area : qAsConst(areas)) { - if (!area.isValid()) { - continue; - } - if (QRectF(area.x() * dpr, area.y() * dpr, area.width() * dpr, area.height() * dpr) - .contains(mousePos)) { - return true; - } - } - return false; - }; const auto isInSpecificObjects = [](const QPointF &mousePos, const QObjectList &objects, const qreal dpr) -> bool { if (objects.isEmpty()) { @@ -1753,9 +1299,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, if (!object) { continue; } - const bool isWidget = object->inherits("QWidget"); - const bool isQuickItem = object->inherits("QQuickItem"); - if (!isWidget && !isQuickItem) { + if (!object->isWidgetType() && !object->inherits("QQuickItem")) { qWarning() << object << "is not a QWidget or QQuickItem!"; continue; } @@ -1777,33 +1321,20 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, } return false; }; - const qreal dpr = GetDevicePixelRatioForWindow(msg->hwnd); - const QPointF globalMouse = QCursor::pos() * dpr; + const qreal dpr = window->devicePixelRatio(); + const QPointF globalMouse = QCursor::pos(window->screen()) * dpr; POINT winLocalMouse = {qRound(globalMouse.x()), qRound(globalMouse.y())}; WNEF_EXECUTE_WINAPI(ScreenToClient, msg->hwnd, &winLocalMouse) const QPointF localMouse = {static_cast(winLocalMouse.x), static_cast(winLocalMouse.y)}; - const bool isInIgnoreAreas = isInSpecificAreas(localMouse, data->ignoreAreas, dpr); - const bool customDragAreas = !data->draggableAreas.isEmpty(); - const bool isInDraggableAreas = customDragAreas ? isInSpecificAreas(localMouse, - data->draggableAreas, - dpr) - : true; - const bool isInIgnoreObjects = isInSpecificObjects(globalMouse, data->ignoreObjects, dpr); - const bool customDragObjects = !data->draggableObjects.isEmpty(); - const bool isInDraggableObjects = customDragObjects - ? isInSpecificObjects(globalMouse, - data->draggableObjects, - dpr) - : true; - const bool customDrag = customDragAreas || customDragObjects; - const bool isResizePermitted = !isInIgnoreAreas && !isInIgnoreObjects; + const bool isInIgnoreObjects = isInSpecificObjects(globalMouse, + qvariant_cast( + window->property(m_ignoredObjects)), + dpr); const int bh = getSystemMetric(window, SystemMetric::BorderHeight, true); const int tbh = getSystemMetric(window, SystemMetric::TitleBarHeight, true); - const bool isTitleBar = (customDrag ? (isInDraggableAreas && isInDraggableObjects) - : (localMouse.y() <= tbh)) - && isResizePermitted && !data->disableTitleBar; - const bool isTop = (localMouse.y() <= bh) && isResizePermitted; + const bool isTitleBar = (localMouse.y() <= tbh) && !isInIgnoreObjects; + const bool isTop = localMouse.y() <= bh; if (shouldHaveWindowFrame()) { // This will handle the left, right and bottom parts of the frame // because we didn't change them. @@ -1834,7 +1365,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, return true; } else { const auto getHitTestResult = - [msg, isTitleBar, &localMouse, bh, isTop, data, window]() -> LRESULT { + [msg, isTitleBar, &localMouse, bh, isTop, window]() -> LRESULT { RECT clientRect = {0, 0, 0, 0}; WNEF_EXECUTE_WINAPI(GetClientRect, msg->hwnd, &clientRect) const LONG ww = clientRect.right; @@ -1852,7 +1383,17 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, const int factor = (isTop || isBottom) ? 2 : 1; const bool isLeft = (localMouse.x() <= (bw * factor)); const bool isRight = (localMouse.x() >= (ww - (bw * factor))); - const bool fixedSize = data->fixedSize; + const bool fixedSize = [window] { + if (window->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint)) { + return true; + } + const QSize minSize = window->minimumSize(); + const QSize maxSize = window->maximumSize(); + if (!minSize.isEmpty() && !maxSize.isEmpty() && minSize == maxSize) { + return true; + } + return false; + }(); const auto getBorderValue = [fixedSize](int value) -> int { // HTBORDER: non-resizable window border. return fixedSize ? HTBORDER : value; @@ -1890,42 +1431,6 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, return true; } } - case WM_GETMINMAXINFO: { - // We can set the maximum and minimum size of the window in this - // message. - const MONITORINFO monitorInfo = GetMonitorInfoForWindow(msg->hwnd); - const RECT rcWorkArea = monitorInfo.rcWork; - const RECT rcMonitorArea = monitorInfo.rcMonitor; - const auto mmi = reinterpret_cast(msg->lParam); - if (isWin8OrGreater()) { - // Works fine on Windows 8/8.1/10 - mmi->ptMaxPosition.x = qAbs(rcWorkArea.left - rcMonitorArea.left); - mmi->ptMaxPosition.y = qAbs(rcWorkArea.top - rcMonitorArea.top); - } else { - // ### FIXME: Buggy on Windows 7: - // The origin of coordinates is the top left edge of the - // monitor's work area. Why? It should be the top left edge of - // the monitor's area. - mmi->ptMaxPosition.x = rcMonitorArea.left; - mmi->ptMaxPosition.y = rcMonitorArea.top; - } - if (!data->maximumSize.isEmpty()) { - mmi->ptMaxSize.x = qRound(GetDevicePixelRatioForWindow(msg->hwnd) - * data->maximumSize.width()); - mmi->ptMaxSize.y = qRound(GetDevicePixelRatioForWindow(msg->hwnd) - * data->maximumSize.height()); - mmi->ptMaxTrackSize.x = mmi->ptMaxSize.x; - mmi->ptMaxTrackSize.y = mmi->ptMaxSize.y; - } - if (!data->minimumSize.isEmpty()) { - mmi->ptMinTrackSize.x = qRound(GetDevicePixelRatioForWindow(msg->hwnd) - * data->minimumSize.width()); - mmi->ptMinTrackSize.y = qRound(GetDevicePixelRatioForWindow(msg->hwnd) - * data->minimumSize.height()); - } - *result = 0; - return true; - } case WM_SETICON: case WM_SETTEXT: { if (shouldUseNativeTitleBar()) { @@ -1938,7 +1443,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, // Prevent Windows from drawing the default title bar by temporarily // toggling the WS_VISIBLE style. WNEF_EXECUTE_WINAPI(SetWindowLongPtrW, msg->hwnd, GWL_STYLE, oldStyle & ~WS_VISIBLE) - updateWindow(window, true, false); + triggerFrameChange(window); const LRESULT ret = WNEF_EXECUTE_WINAPI_RETURN(DefWindowProcW, 0, msg->hwnd, @@ -1946,125 +1451,32 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType, msg->wParam, msg->lParam); WNEF_EXECUTE_WINAPI(SetWindowLongPtrW, msg->hwnd, GWL_STYLE, oldStyle) - updateWindow(window, true, false); + triggerFrameChange(window); *result = ret; return true; } - case WM_ACTIVATE: - case WM_DWMCOMPOSITIONCHANGED: { - if (shouldUseNativeTitleBar()) { - break; - } - - // DWM won't draw the frame shadow if the window doesn't have a - // frame. So extend the window frame a bit to make sure we still - // have the frame shadow. But don't worry, the extended window frame - // won't be seen by the user because it's covered by the client area - // as what we did in WM_NCCALCSIZE. - UpdateFrameMarginsForWindow(msg->hwnd); - break; - } - case WM_DPICHANGED: - // Sent when the effective dots per inch (dpi) for a window has - // changed. You won't get this message until Windows 8.1 - // wParam: The HIWORD of the wParam contains the Y-axis value of - // the new dpi of the window. The LOWORD of the wParam contains - // the X-axis value of the new DPI of the window. For example, - // 96, 120, 144, or 192. The values of the X-axis and the Y-axis - // are identical for Windows apps. - // lParam: A pointer to a RECT structure that provides a suggested - // size and position of the current window scaled for the new DPI. - // The expectation is that apps will reposition and resize windows - // based on the suggestions provided by lParam when handling this - // message. - // Return value: If an application processes this message, it - // should return zero. - // See MSDN for more accurate and detailed information: - // https://docs.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged - // Note: Qt will do the scaling automatically, there is no need - // to do this yourself. See: - // https://code.qt.io/cgit/qt/qtbase.git/tree/src/plugins/platforms/windows/qwindowscontext.cpp - break; - case WM_MOVE: { - if (shouldUseNativeTitleBar()) { - break; - } - - const QString sn = getCurrentScreenIdentifier(msg->hwnd); - if (data->currentScreen.toUpper() != sn) { - data->currentScreen = sn; - updateWindow(window, true, true); - } - break; - } default: break; } return false; } -void WinNativeEventFilter::setWindowData(const QWindow *window, const WINDOWDATA *data) +void WinNativeEventFilter::setBorderWidth(QWindow *window, const int bw) { Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd) && data) { - createUserData(hwnd, data); - } + window->setProperty(m_borderWidth, bw); } -WinNativeEventFilter::WINDOWDATA *WinNativeEventFilter::getWindowData(const QWindow *window) +void WinNativeEventFilter::setBorderHeight(QWindow *window, const int bh) { Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - createUserData(hwnd); - return reinterpret_cast( - WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, hwnd, GWLP_USERDATA)); - } - return nullptr; + window->setProperty(m_borderHeight, bh); } -void WinNativeEventFilter::setBorderWidth(const int bw) -{ - coreData()->m_borderWidth = bw; -} - -void WinNativeEventFilter::setBorderHeight(const int bh) -{ - coreData()->m_borderHeight = bh; -} - -void WinNativeEventFilter::setTitleBarHeight(const int tbh) -{ - coreData()->m_titleBarHeight = tbh; -} - -void WinNativeEventFilter::updateWindow(const QWindow *window, - const bool triggerFrameChange, - const bool redraw) +void WinNativeEventFilter::setTitleBarHeight(QWindow *window, const int tbh) { Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - if (triggerFrameChange) { - WNEF_EXECUTE_WINAPI(SetWindowPos, - hwnd, - nullptr, - 0, - 0, - 0, - 0, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE - | SWP_NOZORDER | SWP_NOOWNERZORDER) - } - if (redraw) { - WNEF_EXECUTE_WINAPI(RedrawWindow, - hwnd, - nullptr, - nullptr, - RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN) - } - } + window->setProperty(m_titleBarHeight, tbh); } int WinNativeEventFilter::getSystemMetric(const QWindow *window, @@ -2073,330 +1485,130 @@ int WinNativeEventFilter::getSystemMetric(const QWindow *window, const bool forceSystemValue) { Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - const qreal dpr = dpiAware ? GetDevicePixelRatioForWindow(hwnd) : m_defaultDevicePixelRatio; - int ret = 0; - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - createUserData(hwnd); - const auto userData = reinterpret_cast( - WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, hwnd, GWLP_USERDATA)); - switch (metric) { - case SystemMetric::BorderWidth: { - const int bw = userData->borderWidth; - if ((bw > 0) && !forceSystemValue) { - ret = qRound(bw * dpr); - } else { - const int result_nondpi = GetSystemMetricsForWindow(hwnd, SM_CXSIZEFRAME) - + GetSystemMetricsForWindow(hwnd, SM_CXPADDEDBORDER); - const int result_dpi = GetSystemMetricsForWindow(hwnd, SM_CXSIZEFRAME, true) - + GetSystemMetricsForWindow(hwnd, SM_CXPADDEDBORDER, true); - const int result = dpiAware ? result_dpi : result_nondpi; - ret = result > 0 ? result : qRound(m_defaultBorderWidth * dpr); - } - } break; - case SystemMetric::BorderHeight: { - const int bh = userData->borderHeight; - if ((bh > 0) && !forceSystemValue) { - ret = qRound(bh * dpr); - } else { - const int result_nondpi = GetSystemMetricsForWindow(hwnd, SM_CYSIZEFRAME) - + GetSystemMetricsForWindow(hwnd, SM_CXPADDEDBORDER); - const int result_dpi = GetSystemMetricsForWindow(hwnd, SM_CYSIZEFRAME, true) - + GetSystemMetricsForWindow(hwnd, SM_CXPADDEDBORDER, true); - const int result = dpiAware ? result_dpi : result_nondpi; - ret = result > 0 ? result : qRound(m_defaultBorderHeight * dpr); - } - } break; - case SystemMetric::TitleBarHeight: { - const int tbh = userData->titleBarHeight; - if ((tbh > 0) && !forceSystemValue) { - // Special case: this is the user defined value, - // don't change it and just return it untouched. - return qRound(tbh * dpr); - } else { - const int result_nondpi = GetSystemMetricsForWindow(hwnd, SM_CYCAPTION); - const int result_dpi = GetSystemMetricsForWindow(hwnd, SM_CYCAPTION, true); - const int result = dpiAware ? result_dpi : result_nondpi; - ret = result > 0 ? result : qRound(m_defaultTitleBarHeight * dpr); - } - } break; + const qreal dpr = dpiAware ? window->devicePixelRatio() : m_defaultDevicePixelRatio; + const auto getSystemMetricsForWindow = [dpr](const int index, const bool dpiAware) -> int { + if (coreData()->m_lpGetSystemMetricsForDpi) { + const quint32 dpi = dpiAware ? qRound(m_defaultDotsPerInch * dpr) + : m_defaultDotsPerInch; + return coreData()->m_lpGetSystemMetricsForDpi(index, dpi); + } else { + const int value = WNEF_EXECUTE_WINAPI_RETURN(GetSystemMetrics, 0, index); + return dpiAware ? value : qRound(value / dpr); } - // When dpr = 1.0 (DPI = 96): - // SM_CXSIZEFRAME = SM_CYSIZEFRAME = 4px - // SM_CXPADDEDBORDER = 4px - // SM_CYCAPTION = 23px - // Border Width = Border Height = SM_C(X|Y)SIZEFRAME + SM_CXPADDEDBORDER = 8px - // Title Bar Height = Border Height + SM_CYCAPTION = 31px - // dpr = 1.25 --> Title Bar Height = 38px - // dpr = 1.5 --> Title Bar Height = 45px - // dpr = 1.75 --> Title Bar Height = 51px - ret += (metric == SystemMetric::TitleBarHeight) - ? getSystemMetric(window, SystemMetric::BorderHeight, dpiAware) - : 0; - return ret; - } + }; + int ret = 0; switch (metric) { case SystemMetric::BorderWidth: { - if ((coreData()->m_borderWidth > 0) && !forceSystemValue) { - ret = qRound(coreData()->m_borderWidth * dpr); + const int bw = window->property(m_borderWidth).toInt(); + if ((bw > 0) && !forceSystemValue) { + ret = qRound(bw * dpr); } else { - ret = qRound(m_defaultBorderWidth * dpr); + const int result_nondpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, false) + + getSystemMetricsForWindow(SM_CXPADDEDBORDER, false); + const int result_dpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, true) + + getSystemMetricsForWindow(SM_CXPADDEDBORDER, true); + const int result = dpiAware ? result_dpi : result_nondpi; + ret = result > 0 ? result : qRound(m_defaultBorderWidth * dpr); } } break; case SystemMetric::BorderHeight: { - if ((coreData()->m_borderHeight > 0) && !forceSystemValue) { - ret = qRound(coreData()->m_borderHeight * dpr); + const int bh = window->property(m_borderHeight).toInt(); + if ((bh > 0) && !forceSystemValue) { + ret = qRound(bh * dpr); } else { - ret = qRound(m_defaultBorderHeight * dpr); + const int result_nondpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, false) + + getSystemMetricsForWindow(SM_CXPADDEDBORDER, false); + const int result_dpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, true) + + getSystemMetricsForWindow(SM_CXPADDEDBORDER, true); + const int result = dpiAware ? result_dpi : result_nondpi; + ret = result > 0 ? result : qRound(m_defaultBorderHeight * dpr); } } break; case SystemMetric::TitleBarHeight: { - if ((coreData()->m_titleBarHeight > 0) && !forceSystemValue) { - ret = qRound(coreData()->m_titleBarHeight * dpr); + const int tbh = window->property(m_titleBarHeight).toInt(); + if ((tbh > 0) && !forceSystemValue) { + // Special case: this is the user defined value, + // don't change it and just return it untouched. + return qRound(tbh * dpr); } else { - ret = qRound(m_defaultTitleBarHeight * dpr); + const int result_nondpi = getSystemMetricsForWindow(SM_CYCAPTION, false); + const int result_dpi = getSystemMetricsForWindow(SM_CYCAPTION, true); + const int result = dpiAware ? result_dpi : result_nondpi; + ret = result > 0 ? result : qRound(m_defaultTitleBarHeight * dpr); } } break; } + // When dpr = 1.0 (DPI = 96): + // SM_CXSIZEFRAME = SM_CYSIZEFRAME = 4px + // SM_CXPADDEDBORDER = 4px + // SM_CYCAPTION = 23px + // Border Width = Border Height = SM_C(X|Y)SIZEFRAME + SM_CXPADDEDBORDER = 8px + // Title Bar Height = Border Height + SM_CYCAPTION = 31px + // dpr = 1.25 --> Title Bar Height = 38px + // dpr = 1.5 --> Title Bar Height = 45px + // dpr = 1.75 --> Title Bar Height = 51px + ret += (metric == SystemMetric::TitleBarHeight) + ? getSystemMetric(window, SystemMetric::BorderHeight, dpiAware) + : 0; return ret; } -void WinNativeEventFilter::setWindowGeometry( - const QWindow *window, const int x, const int y, const int width, const int height) -{ - Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd) && (x > 0) && (y > 0) && (width > 0) - && (height > 0)) { - const qreal dpr = GetDevicePixelRatioForWindow(hwnd); - // Why not use SetWindowPos? Actually we can, but MoveWindow - // sends the WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED, WM_MOVE, - // WM_SIZE, and WM_NCCALCSIZE messages to the window. - // SetWindowPos only sends WM_WINDOWPOSCHANGED. - WNEF_EXECUTE_WINAPI(MoveWindow, hwnd, x, y, qRound(width * dpr), qRound(height * dpr), TRUE) - } -} - -void WinNativeEventFilter::moveWindowToDesktopCenter(const QWindow *window) -{ - Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - const WINDOWINFO windowInfo = GetInfoForWindow(hwnd); - const MONITORINFO monitorInfo = GetMonitorInfoForWindow(hwnd); - // If we want to move a window to the center of the desktop, - // I think we should use rcMonitor, the monitor's whole area, - // to calculate the new coordinates of our window, not rcWork. - const LONG mw = qAbs(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left); - const LONG mh = qAbs(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); - const LONG ww = qAbs(windowInfo.rcWindow.right - windowInfo.rcWindow.left); - const LONG wh = qAbs(windowInfo.rcWindow.bottom - windowInfo.rcWindow.top); - WNEF_EXECUTE_WINAPI(MoveWindow, - hwnd, - qRound((mw - ww) / 2.0), - qRound((mh - wh) / 2.0), - ww, - wh, - TRUE) - } -} - -void WinNativeEventFilter::updateQtFrame(QWindow *window, const int titleBarHeight) -{ - Q_ASSERT(window); - if (titleBarHeight >= 0) { - // Reduce top frame to zero since we paint it ourselves. Use - // device pixel to avoid rounding errors. - const QMargins margins = {0, -titleBarHeight, 0, 0}; - const QVariant marginsVar = QVariant::fromValue(margins); - // The dynamic property takes effect when creating the platform - // window. - window->setProperty("_q_windowsCustomMargins", marginsVar); - // If a platform window exists, change via native interface. -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - QPlatformWindow *platformWindow = window->handle(); - if (platformWindow) { - QGuiApplication::platformNativeInterface() - ->setWindowProperty(platformWindow, - QString::fromUtf8("WindowsCustomMargins"), - marginsVar); - } -#else - auto *platformWindow = dynamic_cast( - window->handle()); - if (platformWindow) { - platformWindow->setCustomMargins(margins); - } -#endif - } -} - -bool WinNativeEventFilter::displaySystemMenu(const QWindow *window, const QPointF &pos) -{ - Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - const HMENU hMenu = WNEF_EXECUTE_WINAPI_RETURN(GetSystemMenu, nullptr, hwnd, FALSE); - if (hMenu) { - MENUITEMINFOW mii; - SecureZeroMemory(&mii, sizeof(mii)); - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STATE; - mii.fType = 0; - mii.fState = MF_ENABLED; - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_RESTORE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_SIZE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_MOVE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_MAXIMIZE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_MINIMIZE, FALSE, &mii) - mii.fState = MF_GRAYED; - const auto data = getWindowData(window); - const bool fixedSize = data ? data->fixedSize : false; - if (fixedSize) { - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_SIZE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_MAXIMIZE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_RESTORE, FALSE, &mii) - } else { - if (IsFullScreen(hwnd) || IsMaximized(hwnd)) { - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_SIZE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_MOVE, FALSE, &mii) - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_MAXIMIZE, FALSE, &mii) - } else if (IsMinimized(hwnd)) { - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_MINIMIZE, FALSE, &mii) - } else { - WNEF_EXECUTE_WINAPI(SetMenuItemInfoW, hMenu, SC_RESTORE, FALSE, &mii) - } - } - const QPointF mousePos = pos.isNull() - ? QCursor::pos() * GetDevicePixelRatioForWindow(hwnd) - : pos; - const bool isRightToLeft = QGuiApplication::layoutDirection() == Qt::RightToLeft; - const LPARAM cmd = WNEF_EXECUTE_WINAPI_RETURN(TrackPopupMenu, - 0, - hMenu, - (TPM_LEFTBUTTON | TPM_RIGHTBUTTON - | TPM_RETURNCMD | TPM_TOPALIGN - | (isRightToLeft ? TPM_RIGHTALIGN - : TPM_LEFTALIGN)), - qRound(mousePos.x()), - qRound(mousePos.y()), - 0, - hwnd, - nullptr); - if (cmd) { - WNEF_EXECUTE_WINAPI(PostMessageW, hwnd, WM_SYSCOMMAND, cmd, 0) - return true; - } - } - } - return false; -} - bool WinNativeEventFilter::setBlurEffectEnabled(const QWindow *window, const bool enabled, const QColor &gradientColor) { Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { -#ifdef QT_WIDGETS_LIB - // Is it possible to set a palette to a QWindow? - QWidget *widget = QWidget::find(reinterpret_cast(hwnd)); - if (widget && widget->isTopLevel()) { - // Qt will paint a solid white background to the window, - // it will cover the blurred effect, so we need to - // make the background become totally transparent. Achieve - // this by setting a palette to the window. - QPalette palette = {}; - if (enabled) { - palette.setColor(QPalette::Window, Qt::transparent); - } - widget->setPalette(palette); - } -#endif - if (isWin8OrGreater() && coreData()->m_lpSetWindowCompositionAttribute) { - ACCENT_POLICY accentPolicy; - SecureZeroMemory(&accentPolicy, sizeof(accentPolicy)); - WINDOWCOMPOSITIONATTRIBDATA wcaData; - SecureZeroMemory(&wcaData, sizeof(wcaData)); - wcaData.Attrib = WCA_ACCENT_POLICY; - wcaData.pvData = &accentPolicy; - wcaData.cbData = sizeof(accentPolicy); - if (enabled) { - if (isWin10OrGreater(17134)) { - // Windows 10, version 1803 (10.0.17134) - // It's not allowed to enable the Acrylic effect for Win32 - // applications until Win10 1803. - if (forceEnableAcrylicOnWin10()) { - accentPolicy.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND; - // The gradient color must be set otherwise it'll look - // like a classic blur. Use semi-transparent gradient - // color to get better appearance. - const QColor color = gradientColor.isValid() ? gradientColor : Qt::white; - accentPolicy.GradientColor = qRgba(color.blue(), - color.green(), - color.red(), - color.alpha()); - } else { - // Enabling the Acrylic effect for Win32 windows is - // very buggy and it's a bug of Windows 10 itself so - // it's not fixable from my side. - // So here we switch back to use the classic blur as - // a workaround. - accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND; - } - } else if (isWin10OrGreater(10240)) { - // Windows 10, version 1507 (10.0.10240) - // The initial version of Win10. - accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND; + const auto hwnd = reinterpret_cast(window->winId()); + if (isWin8OrGreater() && coreData()->m_lpSetWindowCompositionAttribute) { + ACCENT_POLICY accentPolicy; + SecureZeroMemory(&accentPolicy, sizeof(accentPolicy)); + WINDOWCOMPOSITIONATTRIBDATA wcaData; + SecureZeroMemory(&wcaData, sizeof(wcaData)); + wcaData.Attrib = WCA_ACCENT_POLICY; + wcaData.pvData = &accentPolicy; + wcaData.cbData = sizeof(accentPolicy); + if (enabled) { + if (isWin10OrGreater(17134)) { + // Windows 10, version 1803 (10.0.17134) + // It's not allowed to enable the Acrylic effect for Win32 + // applications until Win10 1803. + if (forceEnableAcrylicOnWin10()) { + accentPolicy.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND; + // The gradient color must be set otherwise it'll look + // like a classic blur. Use semi-transparent gradient + // color to get better appearance. + const QColor color = gradientColor.isValid() ? gradientColor : Qt::white; + accentPolicy.GradientColor = qRgba(color.blue(), + color.green(), + color.red(), + color.alpha()); } else { - // Windows 8 and 8.1. - accentPolicy.AccentState = ACCENT_ENABLE_TRANSPARENTGRADIENT; + // Enabling the Acrylic effect for Win32 windows is + // very buggy and it's a bug of Windows 10 itself so + // it's not fixable from my side. + // So here we switch back to use the classic blur as + // a workaround. + accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND; } + } else if (isWin10OrGreater(10240)) { + // Windows 10, version 1507 (10.0.10240) + // The initial version of Win10. + accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND; } else { - accentPolicy.AccentState = ACCENT_DISABLED; + // Windows 8 and 8.1. + accentPolicy.AccentState = ACCENT_ENABLE_TRANSPARENTGRADIENT; } - return coreData()->m_lpSetWindowCompositionAttribute(hwnd, &wcaData); } else { - DWM_BLURBEHIND dwmBB; - SecureZeroMemory(&dwmBB, sizeof(dwmBB)); - dwmBB.dwFlags = DWM_BB_ENABLE; - dwmBB.fEnable = enabled ? TRUE : FALSE; - return SUCCEEDED( - WNEF_EXECUTE_WINAPI_RETURN(DwmEnableBlurBehindWindow, E_FAIL, hwnd, &dwmBB)); + accentPolicy.AccentState = ACCENT_DISABLED; } - } - return false; -} - -void WinNativeEventFilter::updateFrameMargins(const QWindow *window) -{ - Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - UpdateFrameMarginsForWindow(hwnd); - } -} - -void WinNativeEventFilter::setWindowResizable(const QWindow *window, const bool resizable) -{ - Q_ASSERT(window); - const HWND hwnd = getRawHandleFromWindow(window); - if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { - const auto data = getWindowData(window); - if (data) { - data->fixedSize = !resizable; - } - const auto originalStyle = WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, hwnd, GWL_STYLE); - const auto keyResizeStyle = WS_MAXIMIZEBOX | WS_THICKFRAME; - const auto keyFixedStyle = WS_DLGFRAME; - const auto resizableStyle = (originalStyle & ~keyFixedStyle) | keyResizeStyle | WS_CAPTION; - const auto fixedSizeStyle = (originalStyle & ~keyResizeStyle) | keyFixedStyle; - WNEF_EXECUTE_WINAPI(SetWindowLongPtrW, - hwnd, - GWL_STYLE, - resizable ? resizableStyle : fixedSizeStyle) - updateWindow(window, true, false); + return coreData()->m_lpSetWindowCompositionAttribute(hwnd, &wcaData); + } else { + DWM_BLURBEHIND dwmBB; + SecureZeroMemory(&dwmBB, sizeof(dwmBB)); + dwmBB.dwFlags = DWM_BB_ENABLE; + dwmBB.fEnable = enabled ? TRUE : FALSE; + return SUCCEEDED( + WNEF_EXECUTE_WINAPI_RETURN(DwmEnableBlurBehindWindow, E_FAIL, hwnd, &dwmBB)); } } @@ -2449,7 +1661,7 @@ bool WinNativeEventFilter::isDarkFrameEnabled(const QWindow *window) if (!isWin10OrGreater(17763)) { return false; } - const HWND hwnd = getRawHandleFromWindow(window); + const auto hwnd = reinterpret_cast(window->winId()); if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { BOOL result = FALSE; const bool ok = SUCCEEDED(WNEF_EXECUTE_WINAPI_RETURN(DwmGetWindowAttribute, diff --git a/winnativeeventfilter.h b/winnativeeventfilter.h index e5176aa..872a472 100644 --- a/winnativeeventfilter.h +++ b/winnativeeventfilter.h @@ -27,9 +27,7 @@ #include "framelesshelper_global.h" #include #include -#include #include -#include QT_BEGIN_NAMESPACE QT_FORWARD_DECLARE_CLASS(QWindow) @@ -50,97 +48,33 @@ class FRAMELESSHELPER_EXPORT WinNativeEventFilter : public QAbstractNativeEventF Q_DISABLE_COPY_MOVE(WinNativeEventFilter) public: - using WINDOWDATA = struct _WINDOWDATA - { - bool initialized = false /* Internal use only, don't modify it from outside */, - fixedSize = false, mouseTransparent = false, restoreDefaultWindowStyle = false, - enableLayeredWindow = false, disableTitleBar = false, enableBlurBehindWindow = false, - framelessModeEnabled = false; - int borderWidth = -1, borderHeight = -1, titleBarHeight = -1; - QList ignoreAreas = {}, draggableAreas = {}; - QObjectList ignoreObjects = {}, draggableObjects = {}; - QSize maximumSize = {}, minimumSize = {}; - QString currentScreen = {}; - }; - enum class SystemMetric { BorderWidth, BorderHeight, TitleBarHeight }; explicit WinNativeEventFilter(); - ~WinNativeEventFilter() override = default; + ~WinNativeEventFilter() override; - // Make the given window become frameless. - // The width and height will be scaled automatically according to DPI. Don't - // scale them yourself. Just pass the original value. If you don't want to - // change them, pass negative values to the parameters. - static void addFramelessWindow(const QWindow *window, - const WINDOWDATA *data = nullptr, - const bool center = false, - const int x = -1, - const int y = -1, - const int width = -1, - const int height = -1); - static void removeFramelessWindow(const QWindow *window); + static void addFramelessWindow(QWindow *window); + static bool isWindowFrameless(const QWindow *window); + static void removeFramelessWindow(QWindow *window); - // Set borderWidth, borderHeight or titleBarHeight to a negative value to - // restore default behavior. - // Note that it can only affect one specific window. - // If you want to change these values globally, use setBorderWidth instead. - static void setWindowData(const QWindow *window, const WINDOWDATA *data); - // You can modify the given window's data directly, it's the same with using - // setWindowData. - static WINDOWDATA *getWindowData(const QWindow *window); + static void setIgnoredObjects(QWindow *window, const QObjectList &objects); + static QObjectList getIgnoredObjects(const QWindow *window); - // Change settings globally, not a specific window. - // These values will be scaled automatically according to DPI, don't scale - // them yourself. Just pass the original value. - static void setBorderWidth(const int bw); - static void setBorderHeight(const int bh); - static void setTitleBarHeight(const int tbh); + static void setBorderWidth(QWindow *window, const int bw); + static void setBorderHeight(QWindow *window, const int bh); + static void setTitleBarHeight(QWindow *window, const int tbh); - // System metric value of the given window (if the pointer is null, - // return the system's standard value). static int getSystemMetric(const QWindow *window, const SystemMetric metric, - const bool dpiAware = false, + const bool dpiAware, const bool forceSystemValue = false); - // Use this function to trigger a frame change event or redraw a - // specific window. Useful when you want to let some changes - // in effect immediately. - static void updateWindow(const QWindow *window, - const bool triggerFrameChange = true, - const bool redraw = true); - - // Change the geometry of a window through Win32 API. - // The width and height will be scaled automatically according to DPI. So - // just pass the original value. - static void setWindowGeometry( - const QWindow *window, const int x, const int y, const int width, const int height); - - // Move the window to the center of the desktop. - static void moveWindowToDesktopCenter(const QWindow *window); - - // Update Qt's internal data about the window frame, otherwise Qt will - // take the size of the window frame into account when anyone is trying to - // change the geometry of the window. That's not what we want. - static void updateQtFrame(QWindow *window, const int titleBarHeight); - - // Display the system context menu. - static bool displaySystemMenu(const QWindow *window, const QPointF &pos = {}); - // Enable or disable the blur effect for a specific window. // On Win10 it's the Acrylic effect. static bool setBlurEffectEnabled(const QWindow *window, - const bool enabled = true, + const bool enabled, const QColor &gradientColor = Qt::white); - // Thin wrapper of DwmExtendFrameIntoClientArea(). - static void updateFrameMargins(const QWindow *window); - - // A resizable window can be resized and maximized, however, a fixed size - // window can only be moved and minimized, it can't be resized and maximized. - static void setWindowResizable(const QWindow *window, const bool resizable = true); - // Query whether colorization is enabled or not. static bool isColorizationEnabled(); @@ -162,9 +96,6 @@ public: // Query whether the transparency effect is enabled or not. static bool isTransparencyEffectEnabled(); - /////////////////////////////////////////////// - /// CORE FUNCTION - THE SOUL OF THIS CODE - /////////////////////////////////////////////// #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; #else