aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickcontrols/qquickattachedpropertypropagator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quickcontrols/qquickattachedpropertypropagator.cpp')
-rw-r--r--src/quickcontrols/qquickattachedpropertypropagator.cpp150
1 files changed, 133 insertions, 17 deletions
diff --git a/src/quickcontrols/qquickattachedpropertypropagator.cpp b/src/quickcontrols/qquickattachedpropertypropagator.cpp
index ab6f47d4cd..cf752a4248 100644
--- a/src/quickcontrols/qquickattachedpropertypropagator.cpp
+++ b/src/quickcontrols/qquickattachedpropertypropagator.cpp
@@ -8,9 +8,12 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
+#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcAttached, "qt.quick.controls.attachedpropertypropagator")
+
/*!
\class QQuickAttachedPropertyPropagator
\brief The QQuickAttachedPropertyPropagator class provides a way to
@@ -41,6 +44,9 @@ QT_BEGIN_NAMESPACE
\li Call \l initialize() in the constructor
\li Define set/inherit/propagate/reset functions for each property as needed
\li Reimplement \l attachedParentChange() to handle property inheritance
+ \li Implement a static \c qmlAttachedProperties function and declare the
+ type as an attached QML type with \l QML_ELEMENT and \l QML_ATTACHED,
+ as detailed in \l {Providing Attached Properties}
\endlist
For an example that demonstrates this in depth, see
@@ -70,31 +76,77 @@ static QQuickAttachedPropertyPropagator *attachedObject(const QMetaObject *type,
*/
static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *ourAttachedType, QObject *objectWeAreAttachedTo)
{
+ qCDebug(lcAttached).noquote() << "findAttachedParent called with" << ourAttachedType->className() << objectWeAreAttachedTo;
+ /*
+ In the Material ComboBox.qml, we have code like this:
+
+ popup: T.Popup {
+ // ...
+ Material.theme: control.Material.theme
+ // ...
+
+ background: Rectangle {
+ //...
+ color: parent.Material.dialogColor
+
+ The Material attached object has to be accessed this way due to
+ deferred execution limitations (see 3e87695fb4b1a5d503c744046e6d9f43a2ae18a6).
+ However, since parent here refers to QQuickPopupItem and not the popup,
+ the color will actually come from the window. If a dark theme was set on
+ the ComboBox, it will not be respected in the background if we don't have
+ the check below.
+ */
+ auto popupItem = qobject_cast<QQuickPopupItem *>(objectWeAreAttachedTo);
+ if (popupItem) {
+ qCDebug(lcAttached).noquote() << "- attachee belongs to popup item" << popupItem << "- checking if it has an attached object";
+ auto popupItemPrivate = QQuickPopupItemPrivate::get(popupItem);
+ QQuickAttachedPropertyPropagator *popupAttached = attachedObject(ourAttachedType, popupItemPrivate->popup);
+ if (popupAttached) {
+ qCDebug(lcAttached).noquote() << "- popup item has attached object" << popupAttached << "- returning";
+ return popupAttached;
+ } else {
+ qCDebug(lcAttached).noquote() << "- popup item does not have attached object";
+ }
+ } else {
+ qCDebug(lcAttached).noquote() << "- attachee does not belong to a popup";
+ }
+
QQuickItem *item = qobject_cast<QQuickItem *>(objectWeAreAttachedTo);
if (item) {
+ qCDebug(lcAttached).noquote() << "- attachee is an item; checking its parent items and popups";
// lookup parent items and popups
QQuickItem *parent = item->parentItem();
while (parent) {
+ qCDebug(lcAttached).noquote() << " - checking parent item" << parent;
QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, parent);
- if (attached)
+ if (attached) {
+ qCDebug(lcAttached).noquote() << " - parent item has attached object" << attached << "- returning";
return attached;
+ }
QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent->parent());
- if (popup)
+ if (popup) {
+ qCDebug(lcAttached).noquote() << " - parent popup has attached object" << attached << "- returning";
return attachedObject(ourAttachedType, popup);
+ }
parent = parent->parentItem();
}
// fallback to item's window
+ qCDebug(lcAttached).noquote() << "- checking parent window" << item->window();
QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, item->window());
- if (attached)
+ if (attached) {
+ qCDebug(lcAttached).noquote() << "- parent window has attached object" << attached << "- returning";
return attached;
+ }
} else {
// lookup popup's window
QQuickPopup *popup = qobject_cast<QQuickPopup *>(objectWeAreAttachedTo);
- if (popup)
+ if (popup) {
+ qCDebug(lcAttached).noquote() << "- attachee is a popup; checking its window";
return attachedObject(ourAttachedType, popup->popupItem()->window());
+ }
}
// lookup parent window
@@ -102,16 +154,20 @@ static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *o
if (window) {
// It doesn't seem like a parent window can be anything but transient in Qt Quick.
QQuickWindow *parentWindow = qobject_cast<QQuickWindow *>(window->transientParent());
+ qCDebug(lcAttached).noquote() << "- attachee is a window; checking its parent window" << parentWindow;
if (parentWindow) {
QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, parentWindow);
- if (attached)
+ if (attached) {
+ qCDebug(lcAttached).noquote() << "- parent window has attached object" << attached << "- returning";
return attached;
+ }
}
}
// fallback to engine (global)
if (objectWeAreAttachedTo) {
QQmlEngine *engine = qmlEngine(objectWeAreAttachedTo);
+ qCDebug(lcAttached).noquote() << "- falling back to engine" << engine;
if (engine) {
QByteArray name = QByteArray("_q_") + ourAttachedType->className();
QQuickAttachedPropertyPropagator *attached = engine->property(name).value<QQuickAttachedPropertyPropagator *>();
@@ -197,6 +253,7 @@ public:
void setAttachedParent(QQuickAttachedPropertyPropagator *parent);
void itemWindowChanged(QQuickWindow *window);
+ void transientParentWindowChanged(QWindow *newTransientParent);
void itemParentChanged(QQuickItem *item, QQuickItem *parent) override;
QList<QQuickAttachedPropertyPropagator *> attachedChildren;
@@ -205,19 +262,23 @@ public:
void QQuickAttachedPropertyPropagatorPrivate::attachTo(QObject *object)
{
- QQuickItem *item = findAttachedItem(object);
- if (item) {
+ if (QQuickItem *item = findAttachedItem(object)) {
connect(item, &QQuickItem::windowChanged, this, &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged);
QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Parent);
+ } else if (auto *window = qobject_cast<QQuickWindow *>(object)) {
+ QObjectPrivate::connect(window, &QWindow::transientParentChanged, this,
+ &QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged);
}
}
void QQuickAttachedPropertyPropagatorPrivate::detachFrom(QObject *object)
{
- QQuickItem *item = findAttachedItem(object);
- if (item) {
+ if (QQuickItem *item = findAttachedItem(object)) {
disconnect(item, &QQuickItem::windowChanged, this, &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged);
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent);
+ } else if (auto *window = qobject_cast<QQuickWindow *>(object)) {
+ QObjectPrivate::disconnect(window, &QWindow::transientParentChanged,
+ this, &QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged);
}
}
@@ -246,31 +307,64 @@ void QQuickAttachedPropertyPropagatorPrivate::setAttachedParent(QQuickAttachedPr
return;
QQuickAttachedPropertyPropagator *oldParent = attachedParent;
- if (attachedParent)
+ qCDebug(lcAttached).noquote() << "setAttachedParent called on" << q << "with parent" << parent;
+ if (attachedParent) {
+ qCDebug(lcAttached).noquote() << "- removing ourselves as an attached child of" << attachedParent;
QQuickAttachedPropertyPropagatorPrivate::get(attachedParent)->attachedChildren.removeOne(q);
+ }
attachedParent = parent;
- if (parent)
+ if (parent) {
+ qCDebug(lcAttached).noquote() << "- adding ourselves as an attached child of" << parent;
QQuickAttachedPropertyPropagatorPrivate::get(parent)->attachedChildren.append(q);
+ }
q->attachedParentChange(parent, oldParent);
}
+/*
+ If there's e.g. code like this:
+
+ Behavior on Material.elevation {}
+
+ The meta type will be something like QQuickMaterialStyle_QML_125,
+ whereas QQmlMetaType::attachedPropertiesFunc only has attached
+ property data for QQuickMaterialStyle (i.e. attached property types
+ created from C++). We work around this by finding the first C++
+ meta object, which works even for attached types created in QML.
+*/
+const QMetaObject *firstCppMetaObject(QQuickAttachedPropertyPropagator *propagator)
+{
+ return QQmlData::ensurePropertyCache(propagator)->firstCppMetaObject();
+}
+
void QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged(QQuickWindow *window)
{
Q_Q(QQuickAttachedPropertyPropagator);
QQuickAttachedPropertyPropagator *attachedParent = nullptr;
- QQuickItem *item = qobject_cast<QQuickItem *>(q->sender());
- if (item)
- attachedParent = findAttachedParent(q->metaObject(), item);
+ qCDebug(lcAttached).noquote() << "window of" << q << "changed to" << window;
+
+ attachedParent = findAttachedParent(firstCppMetaObject(q), q->parent());
+ if (!attachedParent)
+ attachedParent = attachedObject(firstCppMetaObject(q), window);
+ setAttachedParent(attachedParent);
+}
+
+void QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged(QWindow *newTransientParent)
+{
+ Q_Q(QQuickAttachedPropertyPropagator);
+ QQuickAttachedPropertyPropagator *attachedParent = nullptr;
+ qCDebug(lcAttached).noquote() << "transient parent window of" << q << "changed to" << newTransientParent;
+ attachedParent = findAttachedParent(firstCppMetaObject(q), q->parent());
if (!attachedParent)
- attachedParent = attachedObject(q->metaObject(), window);
+ attachedParent = attachedObject(firstCppMetaObject(q), newTransientParent);
setAttachedParent(attachedParent);
}
void QQuickAttachedPropertyPropagatorPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
{
Q_Q(QQuickAttachedPropertyPropagator);
+ Q_UNUSED(item);
Q_UNUSED(parent);
- setAttachedParent(findAttachedParent(q->metaObject(), item));
+ setAttachedParent(findAttachedParent(firstCppMetaObject(q), q->parent()));
}
/*!
@@ -348,13 +442,19 @@ QQuickAttachedPropertyPropagator *QQuickAttachedPropertyPropagator::attachedPare
void QQuickAttachedPropertyPropagator::initialize()
{
Q_D(QQuickAttachedPropertyPropagator);
+ qCDebug(lcAttached) << "initialize called for" << parent() << "- looking for attached parent...";
QQuickAttachedPropertyPropagator *attachedParent = findAttachedParent(metaObject(), parent());
if (attachedParent)
d->setAttachedParent(attachedParent);
const QList<QQuickAttachedPropertyPropagator *> attachedChildren = findAttachedChildren(metaObject(), parent());
- for (QQuickAttachedPropertyPropagator *child : attachedChildren)
+ qCDebug(lcAttached) << "- found" << attachedChildren.size() << "attached children:";
+ for (QQuickAttachedPropertyPropagator *child : attachedChildren) {
+ qCDebug(lcAttached) << " -" << child->parent();
QQuickAttachedPropertyPropagatorPrivate::get(child)->setAttachedParent(this);
+ }
+
+ qCDebug(lcAttached) << "... finished initializing";
}
/*!
@@ -375,6 +475,22 @@ void QQuickAttachedPropertyPropagator::attachedParentChange(QQuickAttachedProper
Q_UNUSED(oldParent);
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickAttachedPropertyPropagator *propagator)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace().noquote();
+ if (!propagator) {
+ debug << "QQuickAttachedPropertyPropagator(nullptr)";
+ return debug;
+ }
+
+ // Cast to QObject to avoid recursion.
+ debug << static_cast<const QObject *>(propagator) << " (which is attached to " << propagator->parent() << ')';
+ return debug;
+}
+#endif
+
QT_END_NAMESPACE
#include "moc_qquickattachedpropertypropagator.cpp"