diff --git a/include/FramelessHelper/Core/framelesshelper_qt.h b/include/FramelessHelper/Core/framelesshelper_qt.h index 317aa8d..a4cb324 100644 --- a/include/FramelessHelper/Core/framelesshelper_qt.h +++ b/include/FramelessHelper/Core/framelesshelper_qt.h @@ -42,6 +42,7 @@ public: ~FramelessHelperQt() override; static void addWindow(const Global::SystemParameters ¶ms); + static void removeWindow(const WId windowId); protected: Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; diff --git a/include/FramelessHelper/Core/framelesshelper_win.h b/include/FramelessHelper/Core/framelesshelper_win.h index dced04d..3fc093e 100644 --- a/include/FramelessHelper/Core/framelesshelper_win.h +++ b/include/FramelessHelper/Core/framelesshelper_win.h @@ -41,6 +41,7 @@ public: ~FramelessHelperWin() override; static void addWindow(const Global::SystemParameters ¶ms); + static void removeWindow(const WId windowId); Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override; }; diff --git a/include/FramelessHelper/Core/framelessmanager.h b/include/FramelessHelper/Core/framelessmanager.h index 3c1a397..7295c0f 100644 --- a/include/FramelessHelper/Core/framelessmanager.h +++ b/include/FramelessHelper/Core/framelessmanager.h @@ -57,6 +57,7 @@ public: public Q_SLOTS: void addWindow(const Global::SystemParameters ¶ms); + void removeWindow(const WId windowId); Q_SIGNALS: void systemThemeChanged(); diff --git a/include/FramelessHelper/Core/private/framelessmanager_p.h b/include/FramelessHelper/Core/private/framelessmanager_p.h index ec53d3b..68b75f3 100644 --- a/include/FramelessHelper/Core/private/framelessmanager_p.h +++ b/include/FramelessHelper/Core/private/framelessmanager_p.h @@ -53,10 +53,13 @@ public: Q_NODISCARD Global::WallpaperAspectStyle wallpaperAspectStyle() const; static void addWindow(const Global::SystemParameters ¶ms); + static void removeWindow(const WId windowId); Q_INVOKABLE void notifySystemThemeHasChangedOrNot(); Q_INVOKABLE void notifyWallpaperHasChangedOrNot(); + Q_NODISCARD static bool usePureQtImplementation(); + private: void initialize(); diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 9397784..54b1651 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -42,10 +42,10 @@ Q_DECLARE_LOGGING_CATEGORY(lcUtilsCommon) namespace Utils { -[[nodiscard]] FRAMELESSHELPER_CORE_API Qt::CursorShape calculateCursorShape(const QWindow *window, - const QPoint &pos); -[[nodiscard]] FRAMELESSHELPER_CORE_API Qt::Edges calculateWindowEdges(const QWindow *window, - const QPoint &pos); +[[nodiscard]] FRAMELESSHELPER_CORE_API + Qt::CursorShape calculateCursorShape(const QWindow *window, const QPoint &pos); +[[nodiscard]] FRAMELESSHELPER_CORE_API + Qt::Edges calculateWindowEdges(const QWindow *window, const QPoint &pos); FRAMELESSHELPER_CORE_API void startSystemMove(QWindow *window, const QPoint &globalPos); FRAMELESSHELPER_CORE_API void startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos); [[nodiscard]] FRAMELESSHELPER_CORE_API QString getSystemButtonIconCode(const Global::SystemButtonType button); diff --git a/include/FramelessHelper/Quick/framelessquickhelper.h b/include/FramelessHelper/Quick/framelessquickhelper.h index 39d13c3..360070f 100644 --- a/include/FramelessHelper/Quick/framelessquickhelper.h +++ b/include/FramelessHelper/Quick/framelessquickhelper.h @@ -50,7 +50,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL) Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL) Q_PROPERTY(QQuickWindow* window READ window NOTIFY windowChanged2 FINAL) - Q_PROPERTY(bool attached READ isAttached NOTIFY attachedChanged FINAL) + Q_PROPERTY(bool extendsContentIntoTitleBar READ isContentExtendedIntoTitleBar WRITE setContentExtendedIntoTitleBar NOTIFY extendsContentIntoTitleBarChanged FINAL) public: explicit FramelessQuickHelper(QQuickItem *parent = nullptr); @@ -62,10 +62,11 @@ public: Q_NODISCARD QQuickItem *titleBarItem() const; Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isBlurBehindWindowEnabled() const; - Q_NODISCARD bool isAttached() const; + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; public Q_SLOTS: - void extendsContentIntoTitleBar(); + [[deprecated("Use the extendsContentIntoTitleBar property instead.")]] void extendsContentIntoTitleBar(); + void setContentExtendedIntoTitleBar(const bool value); void setTitleBarItem(QQuickItem *value); void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); @@ -85,11 +86,11 @@ protected: void itemChange(const ItemChange change, const ItemChangeData &value) override; Q_SIGNALS: + void extendsContentIntoTitleBarChanged(); void titleBarItemChanged(); void windowFixedSizeChanged(); void blurBehindWindowEnabledChanged(); void windowChanged2(); - void attachedChanged(); void ready(); private: diff --git a/include/FramelessHelper/Quick/private/framelessquickhelper_p.h b/include/FramelessHelper/Quick/private/framelessquickhelper_p.h index 7478a68..35c15ed 100644 --- a/include/FramelessHelper/Quick/private/framelessquickhelper_p.h +++ b/include/FramelessHelper/Quick/private/framelessquickhelper_p.h @@ -50,10 +50,14 @@ public: Q_NODISCARD static FramelessQuickHelperPrivate *get(FramelessQuickHelper *pub); Q_NODISCARD static const FramelessQuickHelperPrivate *get(const FramelessQuickHelper *pub); + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; + void setContentExtendedIntoTitleBar(const bool value); + Q_NODISCARD QQuickItem *getTitleBarItem() const; void setTitleBarItem(QQuickItem *value); - void attachToWindow(); + void attach(); + void detach(); void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); void setHitTestVisible(QQuickItem *item, const bool visible = true); void setHitTestVisible(const QRect &rect, const bool visible = true); @@ -75,8 +79,6 @@ public: void setProperty(const QByteArray &name, const QVariant &value); Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {}); - Q_NODISCARD bool isAttached() const; - protected: Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; diff --git a/include/FramelessHelper/Widgets/framelesswidgetshelper.h b/include/FramelessHelper/Widgets/framelesswidgetshelper.h index 57613ac..b1e1550 100644 --- a/include/FramelessHelper/Widgets/framelesswidgetshelper.h +++ b/include/FramelessHelper/Widgets/framelesswidgetshelper.h @@ -44,7 +44,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL) Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL) Q_PROPERTY(QWidget* window READ window NOTIFY windowChanged FINAL) - Q_PROPERTY(bool attached READ isAttached NOTIFY attachedChanged FINAL) + Q_PROPERTY(bool extendsContentIntoTitleBar READ isContentExtendedIntoTitleBar WRITE setContentExtendedIntoTitleBar NOTIFY extendsContentIntoTitleBarChanged FINAL) public: explicit FramelessWidgetsHelper(QObject *parent = nullptr); @@ -56,10 +56,11 @@ public: Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isBlurBehindWindowEnabled() const; Q_NODISCARD QWidget *window() const; - Q_NODISCARD bool isAttached() const; + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; public Q_SLOTS: - void extendsContentIntoTitleBar(); + [[deprecated("Use the extendsContentIntoTitleBar property instead.")]] void extendsContentIntoTitleBar(); + void setContentExtendedIntoTitleBar(const bool value); void setTitleBarWidget(QWidget *widget); void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); @@ -76,11 +77,11 @@ public Q_SLOTS: void setBlurBehindWindowEnabled(const bool value); Q_SIGNALS: + void extendsContentIntoTitleBarChanged(); void titleBarWidgetChanged(); void windowFixedSizeChanged(); void blurBehindWindowEnabledChanged(); void windowChanged(); - void attachedChanged(); void ready(); private: diff --git a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h index 40c5f21..b9b7445 100644 --- a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h +++ b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h @@ -46,10 +46,14 @@ public: Q_NODISCARD static FramelessWidgetsHelperPrivate *get(FramelessWidgetsHelper *pub); Q_NODISCARD static const FramelessWidgetsHelperPrivate *get(const FramelessWidgetsHelper *pub); + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; + void setContentExtendedIntoTitleBar(const bool value); + Q_NODISCARD QWidget *getTitleBarWidget() const; void setTitleBarWidget(QWidget *widget); - void attachToWindow(); + void attach(); + void detach(); void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); void setHitTestVisible(QWidget *widget, const bool visible = true); void setHitTestVisible(const QRect &rect, const bool visible = true); @@ -72,7 +76,6 @@ public: Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {}); Q_NODISCARD QWidget *window() const; - Q_NODISCARD bool isAttached() const; private: Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const; diff --git a/src/core/framelesshelper_qt.cpp b/src/core/framelesshelper_qt.cpp index 6c8d61f..421d38c 100644 --- a/src/core/framelesshelper_qt.cpp +++ b/src/core/framelesshelper_qt.cpp @@ -106,6 +106,24 @@ void FramelessHelperQt::addWindow(const SystemParameters ¶ms) FramelessHelper::Core::setApplicationOSThemeAware(); } +void FramelessHelperQt::removeWindow(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + const QMutexLocker locker(&g_qtHelper()->mutex); + if (!g_qtHelper()->data.contains(windowId)) { + return; + } + const QtHelperData data = g_qtHelper()->data.value(windowId); + g_qtHelper()->data.remove(windowId); + if (QWindow * const window = Utils::findWindow(windowId)) { + window->removeEventFilter(data.eventFilter); + } + delete data.eventFilter; +} + bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) { Q_ASSERT(object); diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 5f71b1f..d39dab6 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -552,6 +552,41 @@ void FramelessHelperWin::addWindow(const SystemParameters ¶ms) } } +void FramelessHelperWin::removeWindow(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + g_win32Helper()->mutex.lock(); + if (!g_win32Helper()->data.contains(windowId)) { + g_win32Helper()->mutex.unlock(); + return; + } + g_win32Helper()->data.remove(windowId); + if (g_win32Helper()->data.isEmpty()) { + if (!g_win32Helper()->nativeEventFilter.isNull()) { + qApp->removeNativeEventFilter(g_win32Helper()->nativeEventFilter.data()); + g_win32Helper()->nativeEventFilter.reset(); + } + } + HWND hwnd = nullptr; + auto it = g_win32Helper()->fallbackTitleBarToParentWindowMapping.constBegin(); + while (it != g_win32Helper()->fallbackTitleBarToParentWindowMapping.constEnd()) { + if (it.value() == windowId) { + const WId key = it.key(); + hwnd = reinterpret_cast(key); + g_win32Helper()->fallbackTitleBarToParentWindowMapping.remove(key); + break; + } + ++it; + } + g_win32Helper()->mutex.unlock(); + if (DestroyWindow(reinterpret_cast(hwnd)) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kDestroyWindow); + } +} + bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) { if ((eventType != kWin32MessageTypeName) || !message || !result) { diff --git a/src/core/framelessmanager.cpp b/src/core/framelessmanager.cpp index 9faaffe..a3d2619 100644 --- a/src/core/framelessmanager.cpp +++ b/src/core/framelessmanager.cpp @@ -54,10 +54,15 @@ Q_LOGGING_CATEGORY(lcFramelessManager, "wangwenx190.framelesshelper.core.framele using namespace Global; +struct FramelessManagerHelperData +{ + QMetaObject::Connection screenChangeConnection = {}; +}; + struct FramelessManagerHelper { QMutex mutex; - QList windowIds = {}; + QHash data = {}; }; Q_GLOBAL_STATIC(FramelessManagerHelper, g_helper) @@ -182,35 +187,35 @@ void FramelessManagerPrivate::addWindow(const SystemParameters ¶ms) } const WId windowId = params.getWindowId(); g_helper()->mutex.lock(); - if (g_helper()->windowIds.contains(windowId)) { + if (g_helper()->data.contains(windowId)) { g_helper()->mutex.unlock(); return; } - g_helper()->windowIds.append(windowId); + g_helper()->data.insert(windowId, {}); g_helper()->mutex.unlock(); - static const bool pureQt = []() -> bool { -#ifdef Q_OS_WINDOWS - return FramelessConfig::instance()->isSet(Option::UseCrossPlatformQtImplementation); -#else - return true; -#endif - }(); + QMetaObject::Connection screenChangeConnection = {}; + static const bool pureQt = usePureQtImplementation(); #ifdef Q_OS_WINDOWS if (!pureQt) { // Work-around Win32 multi-monitor artifacts. QWindow * const window = params.getWindowHandle(); Q_ASSERT(window); - connect(window, &QWindow::screenChanged, window, [windowId, window](QScreen *screen){ - Q_UNUSED(screen); - // Force a WM_NCCALCSIZE event to inform Windows about our custom window frame, - // this is only necessary when the window is being moved cross monitors. - Utils::triggerFrameChange(windowId); - // For some reason the window is not repainted correctly when moving cross monitors, - // we workaround this issue by force a re-paint and re-layout of the window by triggering - // a resize event manually. Although the actual size does not change, the issue we - // observed disappeared indeed, amazingly. - window->resize(window->size()); - }); + if (window) { + g_helper()->mutex.lock(); + g_helper()->data[windowId].screenChangeConnection = + connect(window, &QWindow::screenChanged, window, [windowId, window](QScreen *screen){ + Q_UNUSED(screen); + // Force a WM_NCCALCSIZE event to inform Windows about our custom window frame, + // this is only necessary when the window is being moved cross monitors. + Utils::triggerFrameChange(windowId); + // For some reason the window is not repainted correctly when moving cross monitors, + // we workaround this issue by force a re-paint and re-layout of the window by triggering + // a resize event manually. Although the actual size does not change, the issue we + // observed disappeared indeed, amazingly. + window->resize(window->size()); + }); + g_helper()->mutex.unlock(); + } } #endif if (pureQt) { @@ -225,6 +230,37 @@ void FramelessManagerPrivate::addWindow(const SystemParameters ¶ms) #endif } +void FramelessManagerPrivate::removeWindow(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + g_helper()->mutex.lock(); + if (!g_helper()->data.contains(windowId)) { + g_helper()->mutex.unlock(); + return; + } + const FramelessManagerHelperData data = g_helper()->data.value(windowId); + g_helper()->data.remove(windowId); + g_helper()->mutex.unlock(); + static const bool pureQt = usePureQtImplementation(); +#ifdef Q_OS_WINDOWS + if (!pureQt && data.screenChangeConnection) { + disconnect(data.screenChangeConnection); + } +#endif + if (pureQt) { + FramelessHelperQt::removeWindow(windowId); + } +#ifdef Q_OS_WINDOWS + if (!pureQt) { + FramelessHelperWin::removeWindow(windowId); + } + Utils::uninstallSystemMenuHook(windowId); +#endif +} + void FramelessManagerPrivate::notifySystemThemeHasChangedOrNot() { const QMutexLocker locker(&g_helper()->mutex); @@ -282,6 +318,18 @@ void FramelessManagerPrivate::notifyWallpaperHasChangedOrNot() } } +bool FramelessManagerPrivate::usePureQtImplementation() +{ + static const bool result = []() -> bool { +#ifdef Q_OS_WINDOWS + return FramelessConfig::instance()->isSet(Option::UseCrossPlatformQtImplementation); +#else + return true; +#endif + }(); + return result; +} + void FramelessManagerPrivate::initialize() { const QMutexLocker locker(&g_helper()->mutex); @@ -342,4 +390,10 @@ void FramelessManager::addWindow(const SystemParameters ¶ms) d->addWindow(params); } +void FramelessManager::removeWindow(const WId windowId) +{ + Q_D(FramelessManager); + d->removeWindow(windowId); +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index d2aee17..8a83898 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -1154,46 +1154,56 @@ void Utils::fixupQtInternals(const WId windowId) if (qEnvironmentVariableIntValue(kNoFixQtInternalEnvVar)) { return; } + bool shouldUpdateFrame = false; const auto hwnd = reinterpret_cast(windowId); SetLastError(ERROR_SUCCESS); - const auto oldClassStyle = static_cast(GetClassLongPtrW(hwnd, GCL_STYLE)); - if (oldClassStyle == 0) { + const auto classStyle = static_cast(GetClassLongPtrW(hwnd, GCL_STYLE)); + if (classStyle != 0) { + // CS_HREDRAW/CS_VREDRAW will trigger a repaint event when the window size changes + // horizontally/vertically, which will cause flicker and jitter during window resizing, + // mostly for the applications which do all the painting by themselves (eg: Qt). + // So we remove these flags from the window class here. Qt by default won't add them + // but let's make it extra safe in case the user may add them by accident. + static constexpr const DWORD badClassStyle = (CS_HREDRAW | CS_VREDRAW); + if (classStyle & badClassStyle) { + SetLastError(ERROR_SUCCESS); + if (SetClassLongPtrW(hwnd, GCL_STYLE, + static_cast(classStyle & ~badClassStyle)) == 0) { + WARNING << getSystemErrorMessage(kSetClassLongPtrW); + } else { + shouldUpdateFrame = true; + } + } + } else { WARNING << getSystemErrorMessage(kGetClassLongPtrW); - return; - } - // CS_HREDRAW/CS_VREDRAW will trigger a repaint event when the window size changes - // horizontally/vertically, which will cause flicker and jitter during window - // resizing, mostly for the applications which do all the painting by themselves. - // So we remove these flags from the window class here, Qt by default won't add them - // but let's make it extra safe. - const DWORD newClassStyle = (oldClassStyle & ~(CS_HREDRAW | CS_VREDRAW)); - SetLastError(ERROR_SUCCESS); - if (SetClassLongPtrW(hwnd, GCL_STYLE, static_cast(newClassStyle)) == 0) { - WARNING << getSystemErrorMessage(kSetClassLongPtrW); - return; } SetLastError(ERROR_SUCCESS); - const auto oldWindowStyle = static_cast(GetWindowLongPtrW(hwnd, GWL_STYLE)); - if (oldWindowStyle == 0) { + const auto windowStyle = static_cast(GetWindowLongPtrW(hwnd, GWL_STYLE)); + if (windowStyle != 0) { + // Qt by default adds the "WS_POPUP" flag to all Win32 windows it created and maintained, + // which is not a good thing (although it won't cause any obvious issues in most cases + // either), because popup windows have some different behavior with normal overlapped + // windows, for example, it will affect DWM's default policy. And Qt will also lack some + // necessary window styles in some cases (caused by misconfigured setWindowFlag(s) calls) + // and this will also break the normal functionalities for our windows, so we do the + // correction here unconditionally. + static constexpr const DWORD goodWindowStyle = + (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME); + if (!(windowStyle & goodWindowStyle)) { + SetLastError(ERROR_SUCCESS); + if (SetWindowLongPtrW(hwnd, GWL_STYLE, + static_cast((windowStyle & ~WS_POPUP) | goodWindowStyle)) == 0) { + WARNING << getSystemErrorMessage(kSetWindowLongPtrW); + } else { + shouldUpdateFrame = true; + } + } + } else { WARNING << getSystemErrorMessage(kGetWindowLongPtrW); - return; } - // Qt by default adds the "WS_POPUP" flag to all Win32 windows it created and maintained, - // which is not a good thing (although it won't cause any obvious issues in most cases - // either), because popup windows have some different behavior with normal overlapped - // windows, for example, it will affect DWM's default policy. And Qt will also lack some - // necessary window styles in some cases (caused by misconfigured setWindowFlag(s) calls) - // and this will also break the normal functionalities for our windows, so we do the - // correction here unconditionally. - static constexpr const DWORD normalWindowStyle = - (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME); - const DWORD newWindowStyle = ((oldWindowStyle & ~WS_POPUP) | normalWindowStyle); - SetLastError(ERROR_SUCCESS); - if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast(newWindowStyle)) == 0) { - WARNING << getSystemErrorMessage(kSetWindowLongPtrW); - return; + if (shouldUpdateFrame) { + triggerFrameChange(windowId); } - triggerFrameChange(windowId); } void Utils::startSystemMove(QWindow *window, const QPoint &globalPos) diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index 2c547a9..1c555d1 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -52,7 +52,7 @@ using namespace Global; struct QuickHelperData { - bool attached = false; + bool ready = false; SystemParameters params = {}; QPointer titleBarItem = nullptr; QList> hitTestVisibleItems = {}; @@ -97,11 +97,14 @@ FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q return; } q_ptr = q; - // Workaround a moc limitation. + // Workaround a MOC limitation: we can't emit a signal from the parent class. connect(q_ptr, &FramelessQuickHelper::windowChanged, q_ptr, &FramelessQuickHelper::windowChanged2); } -FramelessQuickHelperPrivate::~FramelessQuickHelperPrivate() = default; +FramelessQuickHelperPrivate::~FramelessQuickHelperPrivate() +{ + detach(); +} FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(FramelessQuickHelper *pub) { @@ -121,6 +124,24 @@ const FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(const Framel return pub->d_func(); } +bool FramelessQuickHelperPrivate::isContentExtendedIntoTitleBar() const +{ + return getWindowData().ready; +} + +void FramelessQuickHelperPrivate::setContentExtendedIntoTitleBar(const bool value) +{ + if (isContentExtendedIntoTitleBar() == value) { + return; + } + if (value) { + attach(); + } else { + detach(); + } + emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("extendsContentIntoTitleBarChanged")); +} + QQuickItem *FramelessQuickHelperPrivate::getTitleBarItem() const { return getWindowData().titleBarItem; @@ -144,7 +165,7 @@ void FramelessQuickHelperPrivate::setTitleBarItem(QQuickItem *value) emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("titleBarItemChanged")); } -void FramelessQuickHelperPrivate::attachToWindow() +void FramelessQuickHelperPrivate::attach() { Q_Q(FramelessQuickHelper); QQuickWindow * const window = q->window(); @@ -154,18 +175,13 @@ void FramelessQuickHelperPrivate::attachToWindow() } g_quickHelper()->mutex.lock(); - QuickHelperData *data = getWindowDataMutable(); - if (!data) { + QuickHelperData * const data = getWindowDataMutable(); + if (!data || data->ready) { g_quickHelper()->mutex.unlock(); return; } - const bool attached = data->attached; g_quickHelper()->mutex.unlock(); - if (attached) { - return; - } - window->installEventFilter(this); SystemParameters params = {}; @@ -208,7 +224,7 @@ void FramelessQuickHelperPrivate::attachToWindow() g_quickHelper()->mutex.lock(); data->params = params; - data->attached = true; + data->ready = true; g_quickHelper()->mutex.unlock(); // We have to wait for a little time before moving the top level window @@ -223,11 +239,27 @@ void FramelessQuickHelperPrivate::attachToWindow() if (FramelessConfig::instance()->isSet(Option::EnableBlurBehindWindow)) { setBlurBehindWindowEnabled(true, {}); } - emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("attachedChanged")); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready")); }); } +void FramelessQuickHelperPrivate::detach() +{ + Q_Q(FramelessQuickHelper); + QQuickWindow * const w = q->window(); + if (!w) { + return; + } + const WId windowId = w->winId(); + const QMutexLocker locker(&g_quickHelper()->mutex); + if (!g_quickHelper()->data.contains(windowId)) { + return; + } + g_quickHelper()->data.remove(windowId); + w->removeEventFilter(this); + FramelessManager::instance()->removeWindow(windowId); +} + void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType) { Q_ASSERT(item); @@ -512,12 +544,6 @@ QVariant FramelessQuickHelperPrivate::getProperty(const QByteArray &name, const return (value.isValid() ? value : defaultValue); } -bool FramelessQuickHelperPrivate::isAttached() const -{ - const QMutexLocker locker(&g_quickHelper()->mutex); - return getWindowData().attached; -} - bool FramelessQuickHelperPrivate::eventFilter(QObject *object, QEvent *event) { Q_ASSERT(object); @@ -838,7 +864,7 @@ FramelessQuickHelper *FramelessQuickHelper::get(QObject *object) instance->setParentItem(parentItem); instance->setParent(parent); // No need to do this here, we'll do it once the item has been assigned to a specific window. - //instance->d_func()->attachToWindow(); + //instance->d_func()->attach(); } return instance; } @@ -870,15 +896,21 @@ bool FramelessQuickHelper::isBlurBehindWindowEnabled() const return d->isBlurBehindWindowEnabled(); } -bool FramelessQuickHelper::isAttached() const +bool FramelessQuickHelper::isContentExtendedIntoTitleBar() const { Q_D(const FramelessQuickHelper); - return d->isAttached(); + return d->isContentExtendedIntoTitleBar(); } void FramelessQuickHelper::extendsContentIntoTitleBar() { - // Intentionally not doing anything here. + setContentExtendedIntoTitleBar(true); +} + +void FramelessQuickHelper::setContentExtendedIntoTitleBar(const bool value) +{ + Q_D(FramelessQuickHelper); + d->setContentExtendedIntoTitleBar(value); } void FramelessQuickHelper::setTitleBarItem(QQuickItem *value) @@ -988,7 +1020,7 @@ void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeD } } Q_D(FramelessQuickHelper); - d->attachToWindow(); + d->attach(); } } diff --git a/src/quick/framelessquickwindow.cpp b/src/quick/framelessquickwindow.cpp index 4c9c48d..6669458 100644 --- a/src/quick/framelessquickwindow.cpp +++ b/src/quick/framelessquickwindow.cpp @@ -162,7 +162,7 @@ void FramelessQuickWindowPrivate::initialize() { Q_Q(FramelessQuickWindow); QQuickItem * const rootItem = q->contentItem(); - FramelessQuickHelper::get(rootItem)->extendsContentIntoTitleBar(); + FramelessQuickHelper::get(rootItem)->setContentExtendedIntoTitleBar(true); m_topBorderRectangle.reset(new QQuickRectangle(rootItem)); m_topBorderRectangle->setZ(999); // Make sure the frame border stays on top of eveything. m_topBorderRectangle->setColor(kDefaultTransparentColor); diff --git a/src/widgets/framelessdialog.cpp b/src/widgets/framelessdialog.cpp index 6966772..333b0f2 100644 --- a/src/widgets/framelessdialog.cpp +++ b/src/widgets/framelessdialog.cpp @@ -71,7 +71,7 @@ const FramelessDialogPrivate *FramelessDialogPrivate::get(const FramelessDialog void FramelessDialogPrivate::initialize() { Q_Q(FramelessDialog); - FramelessWidgetsHelper::get(q)->extendsContentIntoTitleBar(); + FramelessWidgetsHelper::get(q)->setContentExtendedIntoTitleBar(true); m_helper.reset(new WidgetsSharedHelper(this)); m_helper->setup(q); } diff --git a/src/widgets/framelessmainwindow.cpp b/src/widgets/framelessmainwindow.cpp index ea99f37..fdf35a5 100644 --- a/src/widgets/framelessmainwindow.cpp +++ b/src/widgets/framelessmainwindow.cpp @@ -71,7 +71,7 @@ const FramelessMainWindowPrivate *FramelessMainWindowPrivate::get(const Frameles void FramelessMainWindowPrivate::initialize() { Q_Q(FramelessMainWindow); - FramelessWidgetsHelper::get(q)->extendsContentIntoTitleBar(); + FramelessWidgetsHelper::get(q)->setContentExtendedIntoTitleBar(true); m_helper.reset(new WidgetsSharedHelper(this)); m_helper->setup(q); } diff --git a/src/widgets/framelesswidget.cpp b/src/widgets/framelesswidget.cpp index f028c87..f7bc502 100644 --- a/src/widgets/framelesswidget.cpp +++ b/src/widgets/framelesswidget.cpp @@ -71,7 +71,7 @@ const FramelessWidgetPrivate *FramelessWidgetPrivate::get(const FramelessWidget void FramelessWidgetPrivate::initialize() { Q_Q(FramelessWidget); - FramelessWidgetsHelper::get(q)->extendsContentIntoTitleBar(); + FramelessWidgetsHelper::get(q)->setContentExtendedIntoTitleBar(true); m_helper.reset(new WidgetsSharedHelper(this)); m_helper->setup(q); } diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index af353a0..592b56f 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -54,7 +54,7 @@ using namespace Global; struct WidgetsHelperData { - bool attached = false; + bool ready = false; SystemParameters params = {}; QPointer titleBarWidget = nullptr; QList> hitTestVisibleWidgets = {}; @@ -107,7 +107,10 @@ FramelessWidgetsHelperPrivate::FramelessWidgetsHelperPrivate(FramelessWidgetsHel q_ptr = q; } -FramelessWidgetsHelperPrivate::~FramelessWidgetsHelperPrivate() = default; +FramelessWidgetsHelperPrivate::~FramelessWidgetsHelperPrivate() +{ + detach(); +} FramelessWidgetsHelperPrivate *FramelessWidgetsHelperPrivate::get(FramelessWidgetsHelper *pub) { @@ -264,10 +267,9 @@ QWidget *FramelessWidgetsHelperPrivate::window() const return m_window; } -bool FramelessWidgetsHelperPrivate::isAttached() const +bool FramelessWidgetsHelperPrivate::isContentExtendedIntoTitleBar() const { - const QMutexLocker locker(&g_widgetsHelper()->mutex); - return getWindowData().attached; + return getWindowData().ready; } void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget) @@ -333,7 +335,7 @@ void FramelessWidgetsHelperPrivate::setHitTestVisible(const QRect &rect, const b } } -void FramelessWidgetsHelperPrivate::attachToWindow() +void FramelessWidgetsHelperPrivate::attach() { QWidget * const window = findTopLevelWindow(); Q_ASSERT(window); @@ -346,18 +348,13 @@ void FramelessWidgetsHelperPrivate::attachToWindow() m_window = window; g_widgetsHelper()->mutex.lock(); - WidgetsHelperData *data = getWindowDataMutable(); - if (!data) { + WidgetsHelperData * const data = getWindowDataMutable(); + if (!data || data->ready) { g_widgetsHelper()->mutex.unlock(); return; } - const bool attached = data->attached; g_widgetsHelper()->mutex.unlock(); - if (attached) { - return; - } - // Without this flag, Qt will always create an invisible native parent window // for any native widgets which will intercept some win32 messages and confuse // our own native event filter, so to prevent some weired bugs from happening, @@ -405,7 +402,7 @@ void FramelessWidgetsHelperPrivate::attachToWindow() g_widgetsHelper()->mutex.lock(); data->params = params; - data->attached = true; + data->ready = true; g_widgetsHelper()->mutex.unlock(); // We have to wait for a little time before moving the top level window @@ -421,11 +418,39 @@ void FramelessWidgetsHelperPrivate::attachToWindow() setBlurBehindWindowEnabled(true, {}); } emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("windowChanged")); - emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("attachedChanged")); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready")); }); } +void FramelessWidgetsHelperPrivate::detach() +{ + if (!m_window) { + return; + } + const WId windowId = m_window->winId(); + const QMutexLocker locker(&g_widgetsHelper()->mutex); + if (!g_widgetsHelper()->data.contains(windowId)) { + return; + } + g_widgetsHelper()->data.remove(windowId); + FramelessManager::instance()->removeWindow(windowId); + m_window = nullptr; + emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("windowChanged")); +} + +void FramelessWidgetsHelperPrivate::setContentExtendedIntoTitleBar(const bool value) +{ + if (isContentExtendedIntoTitleBar() == value) { + return; + } + if (value) { + attach(); + } else { + detach(); + } + emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("extendsContentIntoTitleBarChanged")); +} + QWidget *FramelessWidgetsHelperPrivate::findTopLevelWindow() const { Q_Q(const FramelessWidgetsHelper); @@ -796,7 +821,7 @@ FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *object) FramelessWidgetsHelper *instance = parent->findChild(); if (!instance) { instance = new FramelessWidgetsHelper(parent); - instance->d_func()->attachToWindow(); + instance->d_func()->attach(); } return instance; } @@ -825,15 +850,21 @@ QWidget *FramelessWidgetsHelper::window() const return d->window(); } -bool FramelessWidgetsHelper::isAttached() const +bool FramelessWidgetsHelper::isContentExtendedIntoTitleBar() const { Q_D(const FramelessWidgetsHelper); - return d->isAttached(); + return d->isContentExtendedIntoTitleBar(); } void FramelessWidgetsHelper::extendsContentIntoTitleBar() { - // Intentionally not doing anything here. + setContentExtendedIntoTitleBar(true); +} + +void FramelessWidgetsHelper::setContentExtendedIntoTitleBar(const bool value) +{ + Q_D(FramelessWidgetsHelper); + d->setContentExtendedIntoTitleBar(value); } void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget)