diff options
Diffstat (limited to 'src/quickcontrols/qquickattachedpropertypropagator.cpp')
-rw-r--r-- | src/quickcontrols/qquickattachedpropertypropagator.cpp | 150 |
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" |