allow user to remove a window from the frameless window list

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-09-20 15:56:59 +08:00
parent 3d3607af7b
commit 47bf4da5ae
19 changed files with 310 additions and 117 deletions

View File

@ -42,6 +42,7 @@ public:
~FramelessHelperQt() override; ~FramelessHelperQt() override;
static void addWindow(const Global::SystemParameters &params); static void addWindow(const Global::SystemParameters &params);
static void removeWindow(const WId windowId);
protected: protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;

View File

@ -41,6 +41,7 @@ public:
~FramelessHelperWin() override; ~FramelessHelperWin() override;
static void addWindow(const Global::SystemParameters &params); static void addWindow(const Global::SystemParameters &params);
static void removeWindow(const WId windowId);
Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override; Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override;
}; };

View File

@ -57,6 +57,7 @@ public:
public Q_SLOTS: public Q_SLOTS:
void addWindow(const Global::SystemParameters &params); void addWindow(const Global::SystemParameters &params);
void removeWindow(const WId windowId);
Q_SIGNALS: Q_SIGNALS:
void systemThemeChanged(); void systemThemeChanged();

View File

@ -53,10 +53,13 @@ public:
Q_NODISCARD Global::WallpaperAspectStyle wallpaperAspectStyle() const; Q_NODISCARD Global::WallpaperAspectStyle wallpaperAspectStyle() const;
static void addWindow(const Global::SystemParameters &params); static void addWindow(const Global::SystemParameters &params);
static void removeWindow(const WId windowId);
Q_INVOKABLE void notifySystemThemeHasChangedOrNot(); Q_INVOKABLE void notifySystemThemeHasChangedOrNot();
Q_INVOKABLE void notifyWallpaperHasChangedOrNot(); Q_INVOKABLE void notifyWallpaperHasChangedOrNot();
Q_NODISCARD static bool usePureQtImplementation();
private: private:
void initialize(); void initialize();

View File

@ -42,10 +42,10 @@ Q_DECLARE_LOGGING_CATEGORY(lcUtilsCommon)
namespace Utils namespace Utils
{ {
[[nodiscard]] FRAMELESSHELPER_CORE_API Qt::CursorShape calculateCursorShape(const QWindow *window, [[nodiscard]] FRAMELESSHELPER_CORE_API
const QPoint &pos); Qt::CursorShape calculateCursorShape(const QWindow *window, const QPoint &pos);
[[nodiscard]] FRAMELESSHELPER_CORE_API Qt::Edges calculateWindowEdges(const QWindow *window, [[nodiscard]] FRAMELESSHELPER_CORE_API
const QPoint &pos); Qt::Edges calculateWindowEdges(const QWindow *window, const QPoint &pos);
FRAMELESSHELPER_CORE_API void startSystemMove(QWindow *window, const QPoint &globalPos); FRAMELESSHELPER_CORE_API void startSystemMove(QWindow *window, const QPoint &globalPos);
FRAMELESSHELPER_CORE_API void startSystemResize(QWindow *window, const Qt::Edges edges, 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); [[nodiscard]] FRAMELESSHELPER_CORE_API QString getSystemButtonIconCode(const Global::SystemButtonType button);

View File

@ -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 windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL)
Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL) Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL)
Q_PROPERTY(QQuickWindow* window READ window NOTIFY windowChanged2 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: public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr); explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
@ -62,10 +62,11 @@ public:
Q_NODISCARD QQuickItem *titleBarItem() const; Q_NODISCARD QQuickItem *titleBarItem() const;
Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isWindowFixedSize() const;
Q_NODISCARD bool isBlurBehindWindowEnabled() const; Q_NODISCARD bool isBlurBehindWindowEnabled() const;
Q_NODISCARD bool isAttached() const; Q_NODISCARD bool isContentExtendedIntoTitleBar() const;
public Q_SLOTS: public Q_SLOTS:
void extendsContentIntoTitleBar(); [[deprecated("Use the extendsContentIntoTitleBar property instead.")]] void extendsContentIntoTitleBar();
void setContentExtendedIntoTitleBar(const bool value);
void setTitleBarItem(QQuickItem *value); void setTitleBarItem(QQuickItem *value);
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
@ -85,11 +86,11 @@ protected:
void itemChange(const ItemChange change, const ItemChangeData &value) override; void itemChange(const ItemChange change, const ItemChangeData &value) override;
Q_SIGNALS: Q_SIGNALS:
void extendsContentIntoTitleBarChanged();
void titleBarItemChanged(); void titleBarItemChanged();
void windowFixedSizeChanged(); void windowFixedSizeChanged();
void blurBehindWindowEnabledChanged(); void blurBehindWindowEnabledChanged();
void windowChanged2(); void windowChanged2();
void attachedChanged();
void ready(); void ready();
private: private:

View File

@ -50,10 +50,14 @@ public:
Q_NODISCARD static FramelessQuickHelperPrivate *get(FramelessQuickHelper *pub); Q_NODISCARD static FramelessQuickHelperPrivate *get(FramelessQuickHelper *pub);
Q_NODISCARD static const FramelessQuickHelperPrivate *get(const 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; Q_NODISCARD QQuickItem *getTitleBarItem() const;
void setTitleBarItem(QQuickItem *value); void setTitleBarItem(QQuickItem *value);
void attachToWindow(); void attach();
void detach();
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
void setHitTestVisible(QQuickItem *item, const bool visible = true); void setHitTestVisible(QQuickItem *item, const bool visible = true);
void setHitTestVisible(const QRect &rect, 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); void setProperty(const QByteArray &name, const QVariant &value);
Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {}); Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {});
Q_NODISCARD bool isAttached() const;
protected: protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;

View File

@ -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 windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL)
Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL) Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL)
Q_PROPERTY(QWidget* window READ window NOTIFY windowChanged 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: public:
explicit FramelessWidgetsHelper(QObject *parent = nullptr); explicit FramelessWidgetsHelper(QObject *parent = nullptr);
@ -56,10 +56,11 @@ public:
Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isWindowFixedSize() const;
Q_NODISCARD bool isBlurBehindWindowEnabled() const; Q_NODISCARD bool isBlurBehindWindowEnabled() const;
Q_NODISCARD QWidget *window() const; Q_NODISCARD QWidget *window() const;
Q_NODISCARD bool isAttached() const; Q_NODISCARD bool isContentExtendedIntoTitleBar() const;
public Q_SLOTS: public Q_SLOTS:
void extendsContentIntoTitleBar(); [[deprecated("Use the extendsContentIntoTitleBar property instead.")]] void extendsContentIntoTitleBar();
void setContentExtendedIntoTitleBar(const bool value);
void setTitleBarWidget(QWidget *widget); void setTitleBarWidget(QWidget *widget);
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
@ -76,11 +77,11 @@ public Q_SLOTS:
void setBlurBehindWindowEnabled(const bool value); void setBlurBehindWindowEnabled(const bool value);
Q_SIGNALS: Q_SIGNALS:
void extendsContentIntoTitleBarChanged();
void titleBarWidgetChanged(); void titleBarWidgetChanged();
void windowFixedSizeChanged(); void windowFixedSizeChanged();
void blurBehindWindowEnabledChanged(); void blurBehindWindowEnabledChanged();
void windowChanged(); void windowChanged();
void attachedChanged();
void ready(); void ready();
private: private:

View File

@ -46,10 +46,14 @@ public:
Q_NODISCARD static FramelessWidgetsHelperPrivate *get(FramelessWidgetsHelper *pub); Q_NODISCARD static FramelessWidgetsHelperPrivate *get(FramelessWidgetsHelper *pub);
Q_NODISCARD static const FramelessWidgetsHelperPrivate *get(const 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; Q_NODISCARD QWidget *getTitleBarWidget() const;
void setTitleBarWidget(QWidget *widget); void setTitleBarWidget(QWidget *widget);
void attachToWindow(); void attach();
void detach();
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
void setHitTestVisible(QWidget *widget, const bool visible = true); void setHitTestVisible(QWidget *widget, const bool visible = true);
void setHitTestVisible(const QRect &rect, 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 QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {});
Q_NODISCARD QWidget *window() const; Q_NODISCARD QWidget *window() const;
Q_NODISCARD bool isAttached() const;
private: private:
Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const; Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const;

View File

@ -106,6 +106,24 @@ void FramelessHelperQt::addWindow(const SystemParameters &params)
FramelessHelper::Core::setApplicationOSThemeAware(); 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) bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
{ {
Q_ASSERT(object); Q_ASSERT(object);

View File

@ -552,6 +552,41 @@ void FramelessHelperWin::addWindow(const SystemParameters &params)
} }
} }
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<HWND>(key);
g_win32Helper()->fallbackTitleBarToParentWindowMapping.remove(key);
break;
}
++it;
}
g_win32Helper()->mutex.unlock();
if (DestroyWindow(reinterpret_cast<HWND>(hwnd)) == FALSE) {
WARNING << Utils::getSystemErrorMessage(kDestroyWindow);
}
}
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result)
{ {
if ((eventType != kWin32MessageTypeName) || !message || !result) { if ((eventType != kWin32MessageTypeName) || !message || !result) {

View File

@ -54,10 +54,15 @@ Q_LOGGING_CATEGORY(lcFramelessManager, "wangwenx190.framelesshelper.core.framele
using namespace Global; using namespace Global;
struct FramelessManagerHelperData
{
QMetaObject::Connection screenChangeConnection = {};
};
struct FramelessManagerHelper struct FramelessManagerHelper
{ {
QMutex mutex; QMutex mutex;
QList<WId> windowIds = {}; QHash<WId, FramelessManagerHelperData> data = {};
}; };
Q_GLOBAL_STATIC(FramelessManagerHelper, g_helper) Q_GLOBAL_STATIC(FramelessManagerHelper, g_helper)
@ -182,35 +187,35 @@ void FramelessManagerPrivate::addWindow(const SystemParameters &params)
} }
const WId windowId = params.getWindowId(); const WId windowId = params.getWindowId();
g_helper()->mutex.lock(); g_helper()->mutex.lock();
if (g_helper()->windowIds.contains(windowId)) { if (g_helper()->data.contains(windowId)) {
g_helper()->mutex.unlock(); g_helper()->mutex.unlock();
return; return;
} }
g_helper()->windowIds.append(windowId); g_helper()->data.insert(windowId, {});
g_helper()->mutex.unlock(); g_helper()->mutex.unlock();
static const bool pureQt = []() -> bool { QMetaObject::Connection screenChangeConnection = {};
#ifdef Q_OS_WINDOWS static const bool pureQt = usePureQtImplementation();
return FramelessConfig::instance()->isSet(Option::UseCrossPlatformQtImplementation);
#else
return true;
#endif
}();
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
if (!pureQt) { if (!pureQt) {
// Work-around Win32 multi-monitor artifacts. // Work-around Win32 multi-monitor artifacts.
QWindow * const window = params.getWindowHandle(); QWindow * const window = params.getWindowHandle();
Q_ASSERT(window); Q_ASSERT(window);
connect(window, &QWindow::screenChanged, window, [windowId, window](QScreen *screen){ if (window) {
Q_UNUSED(screen); g_helper()->mutex.lock();
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame, g_helper()->data[windowId].screenChangeConnection =
// this is only necessary when the window is being moved cross monitors. connect(window, &QWindow::screenChanged, window, [windowId, window](QScreen *screen){
Utils::triggerFrameChange(windowId); Q_UNUSED(screen);
// For some reason the window is not repainted correctly when moving cross monitors, // Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
// we workaround this issue by force a re-paint and re-layout of the window by triggering // this is only necessary when the window is being moved cross monitors.
// a resize event manually. Although the actual size does not change, the issue we Utils::triggerFrameChange(windowId);
// observed disappeared indeed, amazingly. // For some reason the window is not repainted correctly when moving cross monitors,
window->resize(window->size()); // 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 #endif
if (pureQt) { if (pureQt) {
@ -225,6 +230,37 @@ void FramelessManagerPrivate::addWindow(const SystemParameters &params)
#endif #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() void FramelessManagerPrivate::notifySystemThemeHasChangedOrNot()
{ {
const QMutexLocker locker(&g_helper()->mutex); 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() void FramelessManagerPrivate::initialize()
{ {
const QMutexLocker locker(&g_helper()->mutex); const QMutexLocker locker(&g_helper()->mutex);
@ -342,4 +390,10 @@ void FramelessManager::addWindow(const SystemParameters &params)
d->addWindow(params); d->addWindow(params);
} }
void FramelessManager::removeWindow(const WId windowId)
{
Q_D(FramelessManager);
d->removeWindow(windowId);
}
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -1154,46 +1154,56 @@ void Utils::fixupQtInternals(const WId windowId)
if (qEnvironmentVariableIntValue(kNoFixQtInternalEnvVar)) { if (qEnvironmentVariableIntValue(kNoFixQtInternalEnvVar)) {
return; return;
} }
bool shouldUpdateFrame = false;
const auto hwnd = reinterpret_cast<HWND>(windowId); const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
const auto oldClassStyle = static_cast<DWORD>(GetClassLongPtrW(hwnd, GCL_STYLE)); const auto classStyle = static_cast<DWORD>(GetClassLongPtrW(hwnd, GCL_STYLE));
if (oldClassStyle == 0) { 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<LONG_PTR>(classStyle & ~badClassStyle)) == 0) {
WARNING << getSystemErrorMessage(kSetClassLongPtrW);
} else {
shouldUpdateFrame = true;
}
}
} else {
WARNING << getSystemErrorMessage(kGetClassLongPtrW); 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<LONG_PTR>(newClassStyle)) == 0) {
WARNING << getSystemErrorMessage(kSetClassLongPtrW);
return;
} }
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
const auto oldWindowStyle = static_cast<DWORD>(GetWindowLongPtrW(hwnd, GWL_STYLE)); const auto windowStyle = static_cast<DWORD>(GetWindowLongPtrW(hwnd, GWL_STYLE));
if (oldWindowStyle == 0) { 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<LONG_PTR>((windowStyle & ~WS_POPUP) | goodWindowStyle)) == 0) {
WARNING << getSystemErrorMessage(kSetWindowLongPtrW);
} else {
shouldUpdateFrame = true;
}
}
} else {
WARNING << getSystemErrorMessage(kGetWindowLongPtrW); WARNING << getSystemErrorMessage(kGetWindowLongPtrW);
return;
} }
// Qt by default adds the "WS_POPUP" flag to all Win32 windows it created and maintained, if (shouldUpdateFrame) {
// which is not a good thing (although it won't cause any obvious issues in most cases triggerFrameChange(windowId);
// 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<LONG_PTR>(newWindowStyle)) == 0) {
WARNING << getSystemErrorMessage(kSetWindowLongPtrW);
return;
} }
triggerFrameChange(windowId);
} }
void Utils::startSystemMove(QWindow *window, const QPoint &globalPos) void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)

View File

@ -52,7 +52,7 @@ using namespace Global;
struct QuickHelperData struct QuickHelperData
{ {
bool attached = false; bool ready = false;
SystemParameters params = {}; SystemParameters params = {};
QPointer<QQuickItem> titleBarItem = nullptr; QPointer<QQuickItem> titleBarItem = nullptr;
QList<QPointer<QQuickItem>> hitTestVisibleItems = {}; QList<QPointer<QQuickItem>> hitTestVisibleItems = {};
@ -97,11 +97,14 @@ FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q
return; return;
} }
q_ptr = q; 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); connect(q_ptr, &FramelessQuickHelper::windowChanged, q_ptr, &FramelessQuickHelper::windowChanged2);
} }
FramelessQuickHelperPrivate::~FramelessQuickHelperPrivate() = default; FramelessQuickHelperPrivate::~FramelessQuickHelperPrivate()
{
detach();
}
FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(FramelessQuickHelper *pub) FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(FramelessQuickHelper *pub)
{ {
@ -121,6 +124,24 @@ const FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(const Framel
return pub->d_func(); 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 QQuickItem *FramelessQuickHelperPrivate::getTitleBarItem() const
{ {
return getWindowData().titleBarItem; return getWindowData().titleBarItem;
@ -144,7 +165,7 @@ void FramelessQuickHelperPrivate::setTitleBarItem(QQuickItem *value)
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("titleBarItemChanged")); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("titleBarItemChanged"));
} }
void FramelessQuickHelperPrivate::attachToWindow() void FramelessQuickHelperPrivate::attach()
{ {
Q_Q(FramelessQuickHelper); Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window(); QQuickWindow * const window = q->window();
@ -154,18 +175,13 @@ void FramelessQuickHelperPrivate::attachToWindow()
} }
g_quickHelper()->mutex.lock(); g_quickHelper()->mutex.lock();
QuickHelperData *data = getWindowDataMutable(); QuickHelperData * const data = getWindowDataMutable();
if (!data) { if (!data || data->ready) {
g_quickHelper()->mutex.unlock(); g_quickHelper()->mutex.unlock();
return; return;
} }
const bool attached = data->attached;
g_quickHelper()->mutex.unlock(); g_quickHelper()->mutex.unlock();
if (attached) {
return;
}
window->installEventFilter(this); window->installEventFilter(this);
SystemParameters params = {}; SystemParameters params = {};
@ -208,7 +224,7 @@ void FramelessQuickHelperPrivate::attachToWindow()
g_quickHelper()->mutex.lock(); g_quickHelper()->mutex.lock();
data->params = params; data->params = params;
data->attached = true; data->ready = true;
g_quickHelper()->mutex.unlock(); g_quickHelper()->mutex.unlock();
// We have to wait for a little time before moving the top level window // 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)) { if (FramelessConfig::instance()->isSet(Option::EnableBlurBehindWindow)) {
setBlurBehindWindowEnabled(true, {}); setBlurBehindWindowEnabled(true, {});
} }
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("attachedChanged"));
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready")); 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) void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
{ {
Q_ASSERT(item); Q_ASSERT(item);
@ -512,12 +544,6 @@ QVariant FramelessQuickHelperPrivate::getProperty(const QByteArray &name, const
return (value.isValid() ? value : defaultValue); 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) bool FramelessQuickHelperPrivate::eventFilter(QObject *object, QEvent *event)
{ {
Q_ASSERT(object); Q_ASSERT(object);
@ -838,7 +864,7 @@ FramelessQuickHelper *FramelessQuickHelper::get(QObject *object)
instance->setParentItem(parentItem); instance->setParentItem(parentItem);
instance->setParent(parent); instance->setParent(parent);
// No need to do this here, we'll do it once the item has been assigned to a specific window. // 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; return instance;
} }
@ -870,15 +896,21 @@ bool FramelessQuickHelper::isBlurBehindWindowEnabled() const
return d->isBlurBehindWindowEnabled(); return d->isBlurBehindWindowEnabled();
} }
bool FramelessQuickHelper::isAttached() const bool FramelessQuickHelper::isContentExtendedIntoTitleBar() const
{ {
Q_D(const FramelessQuickHelper); Q_D(const FramelessQuickHelper);
return d->isAttached(); return d->isContentExtendedIntoTitleBar();
} }
void FramelessQuickHelper::extendsContentIntoTitleBar() 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) void FramelessQuickHelper::setTitleBarItem(QQuickItem *value)
@ -988,7 +1020,7 @@ void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeD
} }
} }
Q_D(FramelessQuickHelper); Q_D(FramelessQuickHelper);
d->attachToWindow(); d->attach();
} }
} }

View File

@ -162,7 +162,7 @@ void FramelessQuickWindowPrivate::initialize()
{ {
Q_Q(FramelessQuickWindow); Q_Q(FramelessQuickWindow);
QQuickItem * const rootItem = q->contentItem(); QQuickItem * const rootItem = q->contentItem();
FramelessQuickHelper::get(rootItem)->extendsContentIntoTitleBar(); FramelessQuickHelper::get(rootItem)->setContentExtendedIntoTitleBar(true);
m_topBorderRectangle.reset(new QQuickRectangle(rootItem)); m_topBorderRectangle.reset(new QQuickRectangle(rootItem));
m_topBorderRectangle->setZ(999); // Make sure the frame border stays on top of eveything. m_topBorderRectangle->setZ(999); // Make sure the frame border stays on top of eveything.
m_topBorderRectangle->setColor(kDefaultTransparentColor); m_topBorderRectangle->setColor(kDefaultTransparentColor);

View File

@ -71,7 +71,7 @@ const FramelessDialogPrivate *FramelessDialogPrivate::get(const FramelessDialog
void FramelessDialogPrivate::initialize() void FramelessDialogPrivate::initialize()
{ {
Q_Q(FramelessDialog); Q_Q(FramelessDialog);
FramelessWidgetsHelper::get(q)->extendsContentIntoTitleBar(); FramelessWidgetsHelper::get(q)->setContentExtendedIntoTitleBar(true);
m_helper.reset(new WidgetsSharedHelper(this)); m_helper.reset(new WidgetsSharedHelper(this));
m_helper->setup(q); m_helper->setup(q);
} }

View File

@ -71,7 +71,7 @@ const FramelessMainWindowPrivate *FramelessMainWindowPrivate::get(const Frameles
void FramelessMainWindowPrivate::initialize() void FramelessMainWindowPrivate::initialize()
{ {
Q_Q(FramelessMainWindow); Q_Q(FramelessMainWindow);
FramelessWidgetsHelper::get(q)->extendsContentIntoTitleBar(); FramelessWidgetsHelper::get(q)->setContentExtendedIntoTitleBar(true);
m_helper.reset(new WidgetsSharedHelper(this)); m_helper.reset(new WidgetsSharedHelper(this));
m_helper->setup(q); m_helper->setup(q);
} }

View File

@ -71,7 +71,7 @@ const FramelessWidgetPrivate *FramelessWidgetPrivate::get(const FramelessWidget
void FramelessWidgetPrivate::initialize() void FramelessWidgetPrivate::initialize()
{ {
Q_Q(FramelessWidget); Q_Q(FramelessWidget);
FramelessWidgetsHelper::get(q)->extendsContentIntoTitleBar(); FramelessWidgetsHelper::get(q)->setContentExtendedIntoTitleBar(true);
m_helper.reset(new WidgetsSharedHelper(this)); m_helper.reset(new WidgetsSharedHelper(this));
m_helper->setup(q); m_helper->setup(q);
} }

View File

@ -54,7 +54,7 @@ using namespace Global;
struct WidgetsHelperData struct WidgetsHelperData
{ {
bool attached = false; bool ready = false;
SystemParameters params = {}; SystemParameters params = {};
QPointer<QWidget> titleBarWidget = nullptr; QPointer<QWidget> titleBarWidget = nullptr;
QList<QPointer<QWidget>> hitTestVisibleWidgets = {}; QList<QPointer<QWidget>> hitTestVisibleWidgets = {};
@ -107,7 +107,10 @@ FramelessWidgetsHelperPrivate::FramelessWidgetsHelperPrivate(FramelessWidgetsHel
q_ptr = q; q_ptr = q;
} }
FramelessWidgetsHelperPrivate::~FramelessWidgetsHelperPrivate() = default; FramelessWidgetsHelperPrivate::~FramelessWidgetsHelperPrivate()
{
detach();
}
FramelessWidgetsHelperPrivate *FramelessWidgetsHelperPrivate::get(FramelessWidgetsHelper *pub) FramelessWidgetsHelperPrivate *FramelessWidgetsHelperPrivate::get(FramelessWidgetsHelper *pub)
{ {
@ -264,10 +267,9 @@ QWidget *FramelessWidgetsHelperPrivate::window() const
return m_window; return m_window;
} }
bool FramelessWidgetsHelperPrivate::isAttached() const bool FramelessWidgetsHelperPrivate::isContentExtendedIntoTitleBar() const
{ {
const QMutexLocker locker(&g_widgetsHelper()->mutex); return getWindowData().ready;
return getWindowData().attached;
} }
void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget) 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(); QWidget * const window = findTopLevelWindow();
Q_ASSERT(window); Q_ASSERT(window);
@ -346,18 +348,13 @@ void FramelessWidgetsHelperPrivate::attachToWindow()
m_window = window; m_window = window;
g_widgetsHelper()->mutex.lock(); g_widgetsHelper()->mutex.lock();
WidgetsHelperData *data = getWindowDataMutable(); WidgetsHelperData * const data = getWindowDataMutable();
if (!data) { if (!data || data->ready) {
g_widgetsHelper()->mutex.unlock(); g_widgetsHelper()->mutex.unlock();
return; return;
} }
const bool attached = data->attached;
g_widgetsHelper()->mutex.unlock(); g_widgetsHelper()->mutex.unlock();
if (attached) {
return;
}
// Without this flag, Qt will always create an invisible native parent window // Without this flag, Qt will always create an invisible native parent window
// for any native widgets which will intercept some win32 messages and confuse // 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, // our own native event filter, so to prevent some weired bugs from happening,
@ -405,7 +402,7 @@ void FramelessWidgetsHelperPrivate::attachToWindow()
g_widgetsHelper()->mutex.lock(); g_widgetsHelper()->mutex.lock();
data->params = params; data->params = params;
data->attached = true; data->ready = true;
g_widgetsHelper()->mutex.unlock(); g_widgetsHelper()->mutex.unlock();
// We have to wait for a little time before moving the top level window // We have to wait for a little time before moving the top level window
@ -421,11 +418,39 @@ void FramelessWidgetsHelperPrivate::attachToWindow()
setBlurBehindWindowEnabled(true, {}); setBlurBehindWindowEnabled(true, {});
} }
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("windowChanged")); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("windowChanged"));
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("attachedChanged"));
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready")); 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 QWidget *FramelessWidgetsHelperPrivate::findTopLevelWindow() const
{ {
Q_Q(const FramelessWidgetsHelper); Q_Q(const FramelessWidgetsHelper);
@ -796,7 +821,7 @@ FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *object)
FramelessWidgetsHelper *instance = parent->findChild<FramelessWidgetsHelper *>(); FramelessWidgetsHelper *instance = parent->findChild<FramelessWidgetsHelper *>();
if (!instance) { if (!instance) {
instance = new FramelessWidgetsHelper(parent); instance = new FramelessWidgetsHelper(parent);
instance->d_func()->attachToWindow(); instance->d_func()->attach();
} }
return instance; return instance;
} }
@ -825,15 +850,21 @@ QWidget *FramelessWidgetsHelper::window() const
return d->window(); return d->window();
} }
bool FramelessWidgetsHelper::isAttached() const bool FramelessWidgetsHelper::isContentExtendedIntoTitleBar() const
{ {
Q_D(const FramelessWidgetsHelper); Q_D(const FramelessWidgetsHelper);
return d->isAttached(); return d->isContentExtendedIntoTitleBar();
} }
void FramelessWidgetsHelper::extendsContentIntoTitleBar() 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) void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget)