| 1 | // Copyright (C) 2022 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qquickattachedpropertypropagator.h" |
| 5 | |
| 6 | #include <QtCore/qpointer.h> |
| 7 | #include <QtQuick/qquickwindow.h> |
| 8 | #include <QtQuick/private/qquickitem_p.h> |
| 9 | #include <QtQuick/private/qquickitemchangelistener_p.h> |
| 10 | #include <QtQuickTemplates2/private/qquickpopup_p.h> |
| 11 | #include <QtQuickTemplates2/private/qquickpopupitem_p_p.h> |
| 12 | #include <QtQuickTemplates2/private/qquickpopupwindow_p_p.h> |
| 13 | |
| 14 | QT_BEGIN_NAMESPACE |
| 15 | |
| 16 | Q_LOGGING_CATEGORY(lcAttached, "qt.quick.controls.attachedpropertypropagator" ) |
| 17 | |
| 18 | /*! |
| 19 | \class QQuickAttachedPropertyPropagator |
| 20 | \brief The QQuickAttachedPropertyPropagator class provides a way to |
| 21 | propagate attached properties. |
| 22 | \inmodule QtQuickControls2 |
| 23 | \since 6.5 |
| 24 | |
| 25 | In QML, it is possible to |
| 26 | \l {Attached Properties and Attached Signal Handlers}{attach properties and |
| 27 | signal handlers} to objects. \l {Providing Attached Properties} goes into more |
| 28 | detail about how to expose your own C++ attached types. |
| 29 | |
| 30 | QQuickAttachedPropertyPropagator provides an API to propagate attached |
| 31 | properties from a parent object to its children, similar to |
| 32 | \l {Control::}{font} and \l {Item::}{palette} propagation. It supports |
| 33 | propagation through \l {Item}{items}, \l {Popup}{popups}, and |
| 34 | \l {Window}{windows}. |
| 35 | |
| 36 | If propagation of properties is not important, consider using a |
| 37 | \l {QML_SINGLETON}{C++} or |
| 38 | \l {Contents of a Module Definition qmldir File}{QML} singleton instead, |
| 39 | as it is better suited for that use case, and is more efficient in that |
| 40 | it only requires one QObject. |
| 41 | |
| 42 | To implement a custom attached property: |
| 43 | |
| 44 | \list 1 |
| 45 | \li Derive a class that exposes the attached property from |
| 46 | QQuickAttachedPropertyPropagator. |
| 47 | |
| 48 | For example, to implement an attached \c {MyStyle.theme} property, |
| 49 | declare the \c {MyStyle} class: |
| 50 | |
| 51 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.h |
| 52 | \skipto class |
| 53 | \printto { |
| 54 | |
| 55 | \li Call \l initialize() in the constructor of your class: |
| 56 | |
| 57 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 58 | \skipto MyStyle::MyStyle |
| 59 | \printuntil } |
| 60 | |
| 61 | \li Define set/inherit/propagate/reset functions for the attached property |
| 62 | as needed. For example, to define a \c theme attached property: |
| 63 | |
| 64 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 65 | \skipto theme() |
| 66 | \printto MyStyle::themeChange() |
| 67 | |
| 68 | \li Reimplement \l attachedParentChange() to handle property inheritance: |
| 69 | |
| 70 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 71 | \skipto attachedParentChange |
| 72 | \printuntil /^\}/ |
| 73 | |
| 74 | \li Implement a static \c qmlAttachedProperties function and declare the |
| 75 | type as an attached QML type with \l QML_ELEMENT and \l QML_ATTACHED, |
| 76 | as detailed in \l {Providing Attached Properties}: |
| 77 | |
| 78 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 79 | \skipto qmlAttachedProperties |
| 80 | \printuntil } |
| 81 | |
| 82 | \endlist |
| 83 | |
| 84 | The complete implementation is available in |
| 85 | \l {Qt Quick Controls - Attached Style Properties Example}. |
| 86 | |
| 87 | \sa {Styling Qt Quick Controls} |
| 88 | */ |
| 89 | |
| 90 | static QQuickAttachedPropertyPropagator *attachedObject(const QMetaObject *type, QObject *object, bool create = false) |
| 91 | { |
| 92 | if (!object) |
| 93 | return nullptr; |
| 94 | auto func = qmlAttachedPropertiesFunction(object, type); |
| 95 | return qobject_cast<QQuickAttachedPropertyPropagator *>(object: qmlAttachedPropertiesObject(object, func, create)); |
| 96 | } |
| 97 | |
| 98 | /*! |
| 99 | \internal |
| 100 | |
| 101 | Tries to find a QQuickAttachedPropertyPropagator whose type is \a ourAttachedType |
| 102 | and is attached to an ancestor of \a objectWeAreAttachedTo. |
| 103 | |
| 104 | QQuickAttachedPropertyPropagator needs to know who its parent attached object is in |
| 105 | order to inherit attached property values from it. This is called when an |
| 106 | instance of QQuickAttachedPropertyPropagator is created, and whenever |
| 107 | \c {objectWeAreAttachedTo}'s parent changes, for example. |
| 108 | */ |
| 109 | static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *ourAttachedType, QObject *objectWeAreAttachedTo) |
| 110 | { |
| 111 | qCDebug(lcAttached).noquote() << "findAttachedParent called with" << ourAttachedType->className() << objectWeAreAttachedTo; |
| 112 | /* |
| 113 | In the Material ComboBox.qml, we have code like this: |
| 114 | |
| 115 | popup: T.Popup { |
| 116 | // ... |
| 117 | Material.theme: control.Material.theme |
| 118 | // ... |
| 119 | |
| 120 | background: Rectangle { |
| 121 | //... |
| 122 | color: parent.Material.dialogColor |
| 123 | |
| 124 | The Material attached object has to be accessed this way due to |
| 125 | deferred execution limitations (see 3e87695fb4b1a5d503c744046e6d9f43a2ae18a6). |
| 126 | However, since parent here refers to QQuickPopupItem and not the popup, |
| 127 | the color will actually come from the window. If a dark theme was set on |
| 128 | the ComboBox, it will not be respected in the background if we don't have |
| 129 | the check below. |
| 130 | */ |
| 131 | auto = qobject_cast<QQuickPopupItem *>(object: objectWeAreAttachedTo); |
| 132 | if (popupItem) { |
| 133 | qCDebug(lcAttached).noquote() << "- attachee belongs to popup item" << popupItem << "- checking if it has an attached object" ; |
| 134 | auto = QQuickPopupItemPrivate::get(popupItem); |
| 135 | QQuickAttachedPropertyPropagator * = attachedObject(type: ourAttachedType, object: popupItemPrivate->popup); |
| 136 | if (popupAttached) { |
| 137 | qCDebug(lcAttached).noquote() << "- popup item has attached object" << popupAttached << "- returning" ; |
| 138 | return popupAttached; |
| 139 | } else { |
| 140 | qCDebug(lcAttached).noquote() << "- popup item does not have attached object" ; |
| 141 | } |
| 142 | } else { |
| 143 | qCDebug(lcAttached).noquote() << "- attachee does not belong to a popup" ; |
| 144 | } |
| 145 | |
| 146 | QQuickItem *item = qobject_cast<QQuickItem *>(o: objectWeAreAttachedTo); |
| 147 | if (item) { |
| 148 | qCDebug(lcAttached).noquote() << "- attachee is an item; checking its parent items and popups" ; |
| 149 | // lookup parent items and popups |
| 150 | QQuickItem *parent = item->parentItem(); |
| 151 | while (parent) { |
| 152 | qCDebug(lcAttached).noquote() << " - checking parent item" << parent; |
| 153 | QQuickAttachedPropertyPropagator *attached = attachedObject(type: ourAttachedType, object: parent); |
| 154 | if (attached) { |
| 155 | qCDebug(lcAttached).noquote() << " - parent item has attached object" << attached << "- returning" ; |
| 156 | return attached; |
| 157 | } |
| 158 | |
| 159 | QQuickPopup * = qobject_cast<QQuickPopup *>(object: parent->parent()); |
| 160 | if (popup) { |
| 161 | qCDebug(lcAttached).noquote() << " - parent popup has attached object" << attached << "- returning" ; |
| 162 | return attachedObject(type: ourAttachedType, object: popup); |
| 163 | } |
| 164 | |
| 165 | parent = parent->parentItem(); |
| 166 | } |
| 167 | |
| 168 | // fallback to item's window |
| 169 | qCDebug(lcAttached).noquote() << "- checking parent window" << item->window(); |
| 170 | QQuickAttachedPropertyPropagator *attached = attachedObject(type: ourAttachedType, object: item->window()); |
| 171 | if (attached) { |
| 172 | qCDebug(lcAttached).noquote() << "- parent window has attached object" << attached << "- returning" ; |
| 173 | return attached; |
| 174 | } |
| 175 | } else { |
| 176 | // lookup popup's window |
| 177 | QQuickPopup * = qobject_cast<QQuickPopup *>(object: objectWeAreAttachedTo); |
| 178 | if (popup) { |
| 179 | qCDebug(lcAttached).noquote() << "- attachee is a popup; checking its window" ; |
| 180 | auto* = popup->popupItem()->window(); |
| 181 | auto *object = attachedObject(type: ourAttachedType, object: popupWindow); |
| 182 | // Check if the attached object exists for the popup window. If it |
| 183 | // doesn't, use transient parent attached object |
| 184 | if (!object && qobject_cast<QQuickPopupWindow *>(object: popupWindow)) |
| 185 | return attachedObject(type: ourAttachedType, object: popupWindow->transientParent()); |
| 186 | return object; |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | // lookup parent window |
| 191 | QQuickWindow *window = qobject_cast<QQuickWindow *>(object: objectWeAreAttachedTo); |
| 192 | if (window) { |
| 193 | // It doesn't seem like a parent window can be anything but transient in Qt Quick. |
| 194 | QQuickWindow *parentWindow = qobject_cast<QQuickWindow *>(object: window->transientParent()); |
| 195 | qCDebug(lcAttached).noquote() << "- attachee is a window; checking its parent window" << parentWindow; |
| 196 | if (parentWindow) { |
| 197 | QQuickAttachedPropertyPropagator *attached = attachedObject(type: ourAttachedType, object: parentWindow); |
| 198 | if (attached) { |
| 199 | qCDebug(lcAttached).noquote() << "- parent window has attached object" << attached << "- returning" ; |
| 200 | return attached; |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | // fallback to engine (global) |
| 206 | if (objectWeAreAttachedTo) { |
| 207 | QQmlEngine *engine = qmlEngine(objectWeAreAttachedTo); |
| 208 | qCDebug(lcAttached).noquote() << "- falling back to engine" << engine; |
| 209 | if (engine) { |
| 210 | QByteArray name = QByteArray("_q_" ) + ourAttachedType->className(); |
| 211 | QQuickAttachedPropertyPropagator *attached = engine->property(name).value<QQuickAttachedPropertyPropagator *>(); |
| 212 | if (!attached) { |
| 213 | attached = attachedObject(type: ourAttachedType, object: engine, create: true); |
| 214 | engine->setProperty(name, value: QVariant::fromValue(value: attached)); |
| 215 | } |
| 216 | return attached; |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | return nullptr; |
| 221 | } |
| 222 | |
| 223 | static QList<QQuickAttachedPropertyPropagator *> findAttachedChildren(const QMetaObject *type, QObject *object) |
| 224 | { |
| 225 | QList<QQuickAttachedPropertyPropagator *> children; |
| 226 | |
| 227 | QQuickItem *item = qobject_cast<QQuickItem *>(o: object); |
| 228 | if (!item) { |
| 229 | QQuickWindow *window = qobject_cast<QQuickWindow *>(object); |
| 230 | if (window) |
| 231 | item = window->contentItem(); |
| 232 | } |
| 233 | |
| 234 | if (!item) |
| 235 | return children; |
| 236 | |
| 237 | // At this point, "item" could either be an item that the attached object is |
| 238 | // attached to directly, or the contentItem of a window that the attached object |
| 239 | // is attached to. |
| 240 | |
| 241 | // Look for attached properties on items. |
| 242 | const auto childItems = item->childItems(); |
| 243 | for (QQuickItem *child : childItems) { |
| 244 | QQuickAttachedPropertyPropagator *attached = attachedObject(type, object: child); |
| 245 | if (attached) |
| 246 | children += attached; |
| 247 | else |
| 248 | children += findAttachedChildren(type, object: child); |
| 249 | } |
| 250 | |
| 251 | // Look for attached properties on windows. Windows declared in QML |
| 252 | // as children of a Window are QObject-parented to the contentItem (see |
| 253 | // QQuickWindowPrivate::data_append()). Windows declared as children |
| 254 | // of items are QObject-parented to those items. |
| 255 | const auto &windowChildren = item->children(); |
| 256 | for (QObject *child : windowChildren) { |
| 257 | QQuickWindow *childWindow = qobject_cast<QQuickWindow *>(object: child); |
| 258 | if (childWindow) { |
| 259 | QQuickAttachedPropertyPropagator *attached = attachedObject(type, object: childWindow); |
| 260 | if (attached) |
| 261 | children += attached; |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | return children; |
| 266 | } |
| 267 | |
| 268 | static QQuickItem *findAttachedItem(QObject *parent) |
| 269 | { |
| 270 | QQuickItem *item = qobject_cast<QQuickItem *>(o: parent); |
| 271 | if (!item) { |
| 272 | QQuickPopup * = qobject_cast<QQuickPopup *>(object: parent); |
| 273 | if (popup) |
| 274 | item = popup->popupItem(); |
| 275 | } |
| 276 | return item; |
| 277 | } |
| 278 | |
| 279 | class QQuickAttachedPropertyPropagatorPrivate : public QObjectPrivate, public QQuickItemChangeListener |
| 280 | { |
| 281 | public: |
| 282 | Q_DECLARE_PUBLIC(QQuickAttachedPropertyPropagator) |
| 283 | |
| 284 | static QQuickAttachedPropertyPropagatorPrivate *get(QQuickAttachedPropertyPropagator *attachedObject) |
| 285 | { |
| 286 | return attachedObject->d_func(); |
| 287 | } |
| 288 | |
| 289 | void attachTo(QObject *object); |
| 290 | void detachFrom(QObject *object); |
| 291 | void setAttachedParent(QQuickAttachedPropertyPropagator *parent); |
| 292 | |
| 293 | void itemWindowChanged(QQuickWindow *window); |
| 294 | void transientParentWindowChanged(QWindow *newTransientParent); |
| 295 | void itemParentChanged(QQuickItem *item, QQuickItem *parent) override; |
| 296 | |
| 297 | QList<QQuickAttachedPropertyPropagator *> attachedChildren; |
| 298 | QPointer<QQuickAttachedPropertyPropagator> attachedParent; |
| 299 | }; |
| 300 | |
| 301 | void QQuickAttachedPropertyPropagatorPrivate::attachTo(QObject *object) |
| 302 | { |
| 303 | if (QQuickItem *item = findAttachedItem(parent: object)) { |
| 304 | connect(sender: item, signal: &QQuickItem::windowChanged, receiverPrivate: this, slot: &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged); |
| 305 | QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: QQuickItemPrivate::Parent); |
| 306 | } else if (auto *window = qobject_cast<QQuickWindow *>(object)) { |
| 307 | QObjectPrivate::connect(sender: window, signal: &QWindow::transientParentChanged, receiverPrivate: this, |
| 308 | slot: &QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged); |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | void QQuickAttachedPropertyPropagatorPrivate::detachFrom(QObject *object) |
| 313 | { |
| 314 | if (QQuickItem *item = findAttachedItem(parent: object)) { |
| 315 | disconnect(sender: item, signal: &QQuickItem::windowChanged, receiverPrivate: this, slot: &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged); |
| 316 | QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: QQuickItemPrivate::Parent); |
| 317 | } else if (auto *window = qobject_cast<QQuickWindow *>(object)) { |
| 318 | QObjectPrivate::disconnect(sender: window, signal: &QWindow::transientParentChanged, |
| 319 | receiverPrivate: this, slot: &QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged); |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | /*! |
| 324 | \internal |
| 325 | |
| 326 | This function sets the attached parent of this attached object. |
| 327 | |
| 328 | Currently it is called when: |
| 329 | \list |
| 330 | \li The target item's parent changes. |
| 331 | \li The target item's window changes. |
| 332 | \li The attached object is constructed, to set the attached parent |
| 333 | and the attached parent of the attached object children. |
| 334 | \li The attached object is destructed. |
| 335 | \endlist |
| 336 | |
| 337 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 338 | \skipto MyStyle::resetTheme |
| 339 | \printuntil } |
| 340 | */ |
| 341 | void QQuickAttachedPropertyPropagatorPrivate::setAttachedParent(QQuickAttachedPropertyPropagator *parent) |
| 342 | { |
| 343 | Q_Q(QQuickAttachedPropertyPropagator); |
| 344 | if (attachedParent == parent) |
| 345 | return; |
| 346 | |
| 347 | QQuickAttachedPropertyPropagator *oldParent = attachedParent; |
| 348 | qCDebug(lcAttached).noquote() << "setAttachedParent called on" << q << "with parent" << parent; |
| 349 | if (attachedParent) { |
| 350 | qCDebug(lcAttached).noquote() << "- removing ourselves as an attached child of" << attachedParent; |
| 351 | QQuickAttachedPropertyPropagatorPrivate::get(attachedObject: attachedParent)->attachedChildren.removeOne(t: q); |
| 352 | } |
| 353 | attachedParent = parent; |
| 354 | if (parent) { |
| 355 | qCDebug(lcAttached).noquote() << "- adding ourselves as an attached child of" << parent; |
| 356 | QQuickAttachedPropertyPropagatorPrivate::get(attachedObject: parent)->attachedChildren.append(t: q); |
| 357 | } |
| 358 | q->attachedParentChange(newParent: parent, oldParent); |
| 359 | } |
| 360 | |
| 361 | /* |
| 362 | If there's e.g. code like this: |
| 363 | |
| 364 | Behavior on Material.elevation {} |
| 365 | |
| 366 | The meta type will be something like QQuickMaterialStyle_QML_125, |
| 367 | whereas QQmlMetaType::attachedPropertiesFunc only has attached |
| 368 | property data for QQuickMaterialStyle (i.e. attached property types |
| 369 | created from C++). We work around this by finding the first C++ |
| 370 | meta object, which works even for attached types created in QML. |
| 371 | */ |
| 372 | const QMetaObject *firstCppMetaObject(QQuickAttachedPropertyPropagator *propagator) |
| 373 | { |
| 374 | return QQmlData::ensurePropertyCache(object: propagator)->firstCppMetaObject(); |
| 375 | } |
| 376 | |
| 377 | void QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged(QQuickWindow *window) |
| 378 | { |
| 379 | Q_Q(QQuickAttachedPropertyPropagator); |
| 380 | QQuickAttachedPropertyPropagator *attachedParent = nullptr; |
| 381 | qCDebug(lcAttached).noquote() << "window of" << q << "changed to" << window; |
| 382 | |
| 383 | attachedParent = findAttachedParent(ourAttachedType: firstCppMetaObject(propagator: q), objectWeAreAttachedTo: q->parent()); |
| 384 | if (!attachedParent) |
| 385 | attachedParent = attachedObject(type: firstCppMetaObject(propagator: q), object: window); |
| 386 | setAttachedParent(attachedParent); |
| 387 | } |
| 388 | |
| 389 | void QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged(QWindow *newTransientParent) |
| 390 | { |
| 391 | Q_Q(QQuickAttachedPropertyPropagator); |
| 392 | QQuickAttachedPropertyPropagator *attachedParent = nullptr; |
| 393 | qCDebug(lcAttached).noquote() << "transient parent window of" << q << "changed to" << newTransientParent; |
| 394 | attachedParent = findAttachedParent(ourAttachedType: firstCppMetaObject(propagator: q), objectWeAreAttachedTo: q->parent()); |
| 395 | if (!attachedParent) |
| 396 | attachedParent = attachedObject(type: firstCppMetaObject(propagator: q), object: newTransientParent); |
| 397 | setAttachedParent(attachedParent); |
| 398 | } |
| 399 | |
| 400 | void QQuickAttachedPropertyPropagatorPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) |
| 401 | { |
| 402 | Q_Q(QQuickAttachedPropertyPropagator); |
| 403 | Q_UNUSED(item); |
| 404 | Q_UNUSED(parent); |
| 405 | setAttachedParent(findAttachedParent(ourAttachedType: firstCppMetaObject(propagator: q), objectWeAreAttachedTo: q->parent())); |
| 406 | } |
| 407 | |
| 408 | /*! |
| 409 | Constructs a QQuickAttachedPropertyPropagator with the given \a parent. |
| 410 | |
| 411 | The \c parent will be used to find this object's |
| 412 | \l {attachedParent()}{attached parent}. |
| 413 | |
| 414 | Derived classes should call \l initialize() in their constructor. |
| 415 | */ |
| 416 | QQuickAttachedPropertyPropagator::QQuickAttachedPropertyPropagator(QObject *parent) |
| 417 | : QObject(*(new QQuickAttachedPropertyPropagatorPrivate), parent) |
| 418 | { |
| 419 | Q_D(QQuickAttachedPropertyPropagator); |
| 420 | d->attachTo(object: parent); |
| 421 | } |
| 422 | |
| 423 | /*! |
| 424 | Destroys the QQuickAttachedPropertyPropagator. |
| 425 | */ |
| 426 | QQuickAttachedPropertyPropagator::~QQuickAttachedPropertyPropagator() |
| 427 | { |
| 428 | Q_D(QQuickAttachedPropertyPropagator); |
| 429 | d->detachFrom(object: parent()); |
| 430 | d->setAttachedParent(nullptr); |
| 431 | } |
| 432 | |
| 433 | /*! |
| 434 | This function returns the attached children of this attached object. |
| 435 | |
| 436 | The attached children are used when propagating property values: |
| 437 | |
| 438 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 439 | \skipto MyStyle::propagateTheme |
| 440 | \printuntil } |
| 441 | \printuntil } |
| 442 | */ |
| 443 | QList<QQuickAttachedPropertyPropagator *> QQuickAttachedPropertyPropagator::attachedChildren() const |
| 444 | { |
| 445 | Q_D(const QQuickAttachedPropertyPropagator); |
| 446 | return d->attachedChildren; |
| 447 | } |
| 448 | |
| 449 | /*! |
| 450 | This function returns the attached parent of this attached object. |
| 451 | |
| 452 | The attached parent is used when inheriting property values: |
| 453 | |
| 454 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 455 | \skipto MyStyle::resetTheme |
| 456 | \printuntil } |
| 457 | */ |
| 458 | QQuickAttachedPropertyPropagator *QQuickAttachedPropertyPropagator::attachedParent() const |
| 459 | { |
| 460 | Q_D(const QQuickAttachedPropertyPropagator); |
| 461 | return d->attachedParent; |
| 462 | } |
| 463 | |
| 464 | /*! |
| 465 | Finds and sets the attached parent for this attached object, and then does |
| 466 | the same for its children. This must be called upon construction of the |
| 467 | attached object in order for propagation to work. |
| 468 | |
| 469 | It can be useful to read global/default values before calling this |
| 470 | function. For example, before calling \c initialize(), the |
| 471 | \l {Imagine Style}{Imagine} style checks a static "globalsInitialized" flag |
| 472 | to see if it should read default values from \l QSettings. The values from |
| 473 | that file form the basis for any attached property values that have not |
| 474 | been explicitly set. |
| 475 | |
| 476 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 477 | \skipto MyStyle::MyStyle |
| 478 | \printuntil } |
| 479 | */ |
| 480 | void QQuickAttachedPropertyPropagator::initialize() |
| 481 | { |
| 482 | Q_D(QQuickAttachedPropertyPropagator); |
| 483 | qCDebug(lcAttached) << "initialize called for" << parent() << "- looking for attached parent..." ; |
| 484 | QQuickAttachedPropertyPropagator *attachedParent = findAttachedParent(ourAttachedType: metaObject(), objectWeAreAttachedTo: parent()); |
| 485 | if (attachedParent) |
| 486 | d->setAttachedParent(attachedParent); |
| 487 | |
| 488 | const QList<QQuickAttachedPropertyPropagator *> attachedChildren = findAttachedChildren(type: metaObject(), object: parent()); |
| 489 | qCDebug(lcAttached) << "- found" << attachedChildren.size() << "attached children:" ; |
| 490 | for (QQuickAttachedPropertyPropagator *child : attachedChildren) { |
| 491 | qCDebug(lcAttached) << " -" << child->parent(); |
| 492 | QQuickAttachedPropertyPropagatorPrivate::get(attachedObject: child)->setAttachedParent(this); |
| 493 | } |
| 494 | |
| 495 | qCDebug(lcAttached) << "... finished initializing" ; |
| 496 | } |
| 497 | |
| 498 | /*! |
| 499 | This function is called whenever the attached parent of this |
| 500 | QQuickAttachedPropertyPropagator changes from \a oldParent to \a newParent. |
| 501 | |
| 502 | Subclasses should reimplement this function to inherit attached properties |
| 503 | from \c newParent. |
| 504 | |
| 505 | \quotefromfile ../../examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp |
| 506 | \skipto attachedParentChange |
| 507 | \printuntil } |
| 508 | \printuntil } |
| 509 | */ |
| 510 | void QQuickAttachedPropertyPropagator::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) |
| 511 | { |
| 512 | Q_UNUSED(newParent); |
| 513 | Q_UNUSED(oldParent); |
| 514 | } |
| 515 | |
| 516 | #ifndef QT_NO_DEBUG_STREAM |
| 517 | QDebug operator<<(QDebug debug, const QQuickAttachedPropertyPropagator *propagator) |
| 518 | { |
| 519 | QDebugStateSaver saver(debug); |
| 520 | debug.nospace().noquote(); |
| 521 | if (!propagator) { |
| 522 | debug << "QQuickAttachedPropertyPropagator(nullptr)" ; |
| 523 | return debug; |
| 524 | } |
| 525 | |
| 526 | // Cast to QObject to avoid recursion. |
| 527 | debug << static_cast<const QObject *>(propagator) << " (which is attached to " << propagator->parent() << ')'; |
| 528 | return debug; |
| 529 | } |
| 530 | #endif |
| 531 | |
| 532 | QT_END_NAMESPACE |
| 533 | |
| 534 | #include "moc_qquickattachedpropertypropagator.cpp" |
| 535 | |