summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-08-24 12:36:28 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-08-30 15:45:41 +0200
commit4ba7a76985cd52eaab2d0bf133e9ecb93b50e6cc (patch)
tree16181443d6b6052928e305d8bb6f5650efdef13b
parentbb2f551b324f90153364580be48c7c241b723120 (diff)
Allow QObjects to opt in to receiving ParentAboutToChange/ParentChange
QWidget already handles this, but it might be useful for non-Widget object hierarchies as well, such as in Qt Quick. The flag is opt in, and as QWidget already handles these events by itself (without checking any flags), we assert that we don't end up in this code path, instead of enabling it for QWidget. The latter would mean refactoring the QWidget code, with possible regressions. Docs and header comments have been updated to reflect that this event is not widget specific. (This is an issue with other events as well, that are documented to say "widget", since they came from a time when there was only QWidget, but nowadays apply to e.g. QWindow as well. That's something for another fix though). Change-Id: Ib71962131d6011c17dcce8c01bd8adcdaa58d798 Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
-rw-r--r--src/corelib/kernel/qcoreevent.cpp6
-rw-r--r--src/corelib/kernel/qcoreevent.h2
-rw-r--r--src/corelib/kernel/qobject.cpp15
-rw-r--r--src/corelib/kernel/qobject.h3
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp73
5 files changed, 95 insertions, 4 deletions
diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp
index 838c90fce7..d042064579 100644
--- a/src/corelib/kernel/qcoreevent.cpp
+++ b/src/corelib/kernel/qcoreevent.cpp
@@ -159,8 +159,10 @@ Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type);
\value OrientationChange The screens orientation has changes (QScreenOrientationChangeEvent).
\value Paint Screen update necessary (QPaintEvent).
\value PaletteChange Palette of the widget changed.
- \value ParentAboutToChange The widget parent is about to change.
- \value ParentChange The widget parent has changed.
+ \value ParentAboutToChange The object parent is about to change.
+ Only sent to some object types, such as QWidget.
+ \value ParentChange The object parent has changed.
+ Only sent to some object types, such as QWidget.
\value PlatformPanel A platform specific panel has been requested.
\value PlatformSurface A native platform surface has been created or is about to be destroyed (QPlatformSurfaceEvent).
\omitvalue Pointer
diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h
index 93ab1c1871..e8e4b4bba6 100644
--- a/src/corelib/kernel/qcoreevent.h
+++ b/src/corelib/kernel/qcoreevent.h
@@ -77,7 +77,7 @@ public:
Hide = 18, // widget is hidden
Close = 19, // request to close widget
Quit = 20, // request to quit application
- ParentChange = 21, // widget has been reparented
+ ParentChange = 21, // object has been reparented
ParentAboutToChange = 131, // sent just before the parent change is done
ThreadChange = 22, // object has changed threads
WindowActivate = 24, // window was activated
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 90d1f6745c..7c109238b2 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -179,6 +179,7 @@ QObjectPrivate::QObjectPrivate(int version)
isQuickItem = false;
willBeWidget = false;
wasWidget = false;
+ receiveParentEvents = false; // If object wants ParentAboutToChange and ParentChange
}
QObjectPrivate::~QObjectPrivate()
@@ -2249,7 +2250,15 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentAboutToChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
+
parent = o;
+
if (parent) {
// object hierarchies are constrained to a single thread
if (threadData.loadRelaxed() != parent->d_func()->threadData.loadRelaxed()) {
@@ -2265,6 +2274,12 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
}
/*!
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index eb99504647..7c3d74dcda 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -72,7 +72,8 @@ public:
uint isQuickItem : 1;
uint willBeWidget : 1; // for handling widget-specific bits in QObject's ctor
uint wasWidget : 1; // for properly cleaning up in QObject's dtor
- uint unused : 21;
+ uint receiveParentEvents: 1;
+ uint unused : 20;
QAtomicInt postedEvents;
QDynamicMetaObjectData *metaObject;
QBindingStorage bindingStorage;
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index a4f761d6b9..5692615f29 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -80,6 +80,7 @@ private slots:
void signalBlocking();
void blockingQueuedConnection();
void childEvents();
+ void parentEvents();
void installEventFilter();
void deleteSelfInSlot();
void disconnectSelfInSlotAndDeleteAfterEmit();
@@ -3200,6 +3201,78 @@ void tst_QObject::childEvents()
}
}
+void tst_QObject::parentEvents()
+{
+#ifdef QT_BUILD_INTERNAL
+ EventSpy::EventList expected;
+
+ {
+ // Parent events not enabled
+ QObject parent;
+ QObject child;
+
+ EventSpy spy;
+ child.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ child.setParent(&parent);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList();
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&child, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+
+ {
+ // Parent events enabled
+ QObject parent;
+ QObject child;
+ auto *childPrivate = QObjectPrivate::get(&child);
+ childPrivate->receiveParentEvents = true;
+
+ EventSpy spy;
+ child.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ child.setParent(&parent);
+ child.setParent(nullptr);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::ParentAboutToChange)
+ << qMakePair(&child, QEvent::ParentChange)
+ << qMakePair(&child, QEvent::ParentAboutToChange)
+ << qMakePair(&child, QEvent::ParentChange);
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&child, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+#else
+ QSKIP("Needs QT_BUILD_INTERNAL");
+#endif
+}
+
void tst_QObject::installEventFilter()
{
QEvent event(QEvent::User);