1// Copyright (C) 2020 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 "qproperty.h"
5#include "qproperty_p.h"
6
7#include <qscopedvaluerollback.h>
8#include <QScopeGuard>
9#include <QtCore/qalloc.h>
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/private/qthread_p.h>
12#include <QtCore/qmetaobject.h>
13#include <QtCore/qmutex.h>
14
15#include "qobject_p.h"
16
17QT_BEGIN_NAMESPACE
18
19Q_STATIC_LOGGING_CATEGORY(lcQPropertyBinding, "qt.qproperty.binding");
20
21using namespace QtPrivate;
22
23void QPropertyBindingPrivatePtr::destroyAndFreeMemory()
24{
25 QPropertyBindingPrivate::destroyAndFreeMemory(priv: static_cast<QPropertyBindingPrivate *>(d));
26}
27
28void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
29{
30 if (ptr != d) {
31 if (ptr)
32 ptr->addRef();
33 auto *old = std::exchange(obj&: d, new_val&: ptr);
34 if (old && !old->deref())
35 QPropertyBindingPrivate::destroyAndFreeMemory(priv: static_cast<QPropertyBindingPrivate *>(d));
36 }
37}
38
39
40void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
41{
42 if (auto *b = binding()) {
43 observer->prev = &b->firstObserver.ptr;
44 observer->next = b->firstObserver.ptr;
45 if (observer->next)
46 observer->next->prev = &observer->next;
47 b->firstObserver.ptr = observer;
48 } else {
49 auto &d = ptr->d_ref();
50 Q_ASSERT(!(d & QPropertyBindingData::BindingBit));
51 auto firstObserver = reinterpret_cast<QPropertyObserver*>(d);
52 observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
53 observer->next = firstObserver;
54 if (observer->next)
55 observer->next->prev = &observer->next;
56 d = reinterpret_cast<quintptr>(observer);
57 }
58}
59
60/*!
61 \internal
62
63 QPropertyDelayedNotifications is used to manage delayed notifications in grouped property updates.
64 It acts as a pool allocator for QPropertyProxyBindingData, and has methods to manage delayed
65 notifications.
66
67 \sa beginPropertyUpdateGroup, endPropertyUpdateGroup
68*/
69struct QPropertyDelayedNotifications
70{
71 // we can't access the dynamic page size as we need a constant value
72 // use 4096 as a sensible default
73 static constexpr inline auto PageSize = 4096;
74 int ref = 0;
75 QPropertyDelayedNotifications *next = nullptr; // in case we have more than size dirty properties...
76 qsizetype used = 0;
77 // Size chosen to avoid allocating more than one page of memory, while still ensuring
78 // that we can store many delayed properties without doing further allocations
79 static constexpr qsizetype size = (PageSize - 3*sizeof(void *))/sizeof(QPropertyProxyBindingData);
80 QPropertyProxyBindingData delayedProperties[size];
81
82 /*!
83 \internal
84 This method is called when a property attempts to notify its observers while inside of a
85 property update group. Instead of actually notifying, it replaces \a bindingData's d_ptr
86 with a QPropertyProxyBindingData.
87 \a bindingData and \a propertyData are the binding data and property data of the property
88 whose notify call gets delayed.
89 \sa QPropertyBindingData::notifyObservers
90 */
91 void addProperty(const QPropertyBindingData *bindingData, QUntypedPropertyData *propertyData) {
92 if (bindingData->isNotificationDelayed())
93 return;
94 auto *data = this;
95 while (data->used == size) {
96 if (!data->next)
97 // add a new page
98 data->next = new QPropertyDelayedNotifications;
99 data = data->next;
100 }
101 auto *delayed = data->delayedProperties + data->used;
102 *delayed = QPropertyProxyBindingData { .d_ptr: bindingData->d_ptr, .originalBindingData: bindingData, .propertyData: propertyData };
103 ++data->used;
104 // preserve the binding bit for faster access
105 quintptr bindingBit = bindingData->d_ptr & QPropertyBindingData::BindingBit;
106 bindingData->d_ptr = reinterpret_cast<quintptr>(delayed) | QPropertyBindingData::DelayedNotificationBit | bindingBit;
107 Q_ASSERT(bindingData->d_ptr > 3);
108 if (!bindingBit) {
109 if (auto observer = reinterpret_cast<QPropertyObserver *>(delayed->d_ptr))
110 observer->prev = reinterpret_cast<QPropertyObserver **>(&delayed->d_ptr);
111 }
112 }
113
114 /*!
115 \internal
116 Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
117 \a index, it
118 \list
119 \li restores the original binding data that was modified in addProperty and
120 \li evaluates any bindings which depend on properties that were changed inside
121 the group.
122 \endlist
123 Change notifications are sent later with notify (following the logic of separating
124 binding updates and notifications used in non-deferred updates).
125 */
126 void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status) {
127 auto *delayed = delayedProperties + index;
128 auto *bindingData = delayed->originalBindingData;
129 if (!bindingData)
130 return;
131
132 bindingData->d_ptr = delayed->d_ptr;
133 Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
134 if (!bindingData->hasBinding()) {
135 if (auto observer = reinterpret_cast<QPropertyObserver *>(bindingData->d_ptr))
136 observer->prev = reinterpret_cast<QPropertyObserver **>(&bindingData->d_ptr);
137 }
138
139 QPropertyBindingDataPointer bindingDataPointer{.ptr: bindingData};
140 QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
141 if (observer)
142 observer.evaluateBindings(bindingObservers, status);
143 }
144
145 /*!
146 \internal
147 Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
148 \a i, it
149 \list
150 \li resets the proxy binding data and
151 \li sends any pending notifications.
152 \endlist
153 */
154 void notify(qsizetype index) {
155 auto *delayed = delayedProperties + index;
156 if (delayed->d_ptr & QPropertyBindingData::BindingBit)
157 return; // already handled
158 if (!delayed->originalBindingData)
159 return;
160 delayed->originalBindingData = nullptr;
161
162 QPropertyObserverPointer observer { .ptr: reinterpret_cast<QPropertyObserver *>(delayed->d_ptr & ~QPropertyBindingData::DelayedNotificationBit) };
163 delayed->d_ptr = 0;
164
165 if (observer)
166 observer.notify(propertyDataPtr: delayed->propertyData);
167 }
168};
169
170/*
171 The binding status needs some care: Conceptually, it is a thread-local. However, we cache it
172 in QObjects via their QBindingStorage. Those QObjects might outlive the thread in which the
173 binding status was initially created (e.g. when their QThread is stopped).
174 If they are not migrated to another (running) QThread, they would have a stale pointer if
175 a plain thread local were used for the QBindingStatus.
176
177 So instead of a normal thread_local, we use the following scheme:
178 - On first access, the QBindingStatus gets allocated on the heap, and stored in QThreadData
179 - It also gets cached in in a thread_local variable for faster access
180 - The QThreadData takes care of deleting the QBindingStatus in its destructor
181 - Moreover, if a QThread is restarted, the native thread's thread local gets initialized with
182 the QBindingStatus of the QThreadData. Otherwise, we'd somehow need to update all objects
183 whose affinity is pointing to that thread, which we can't easily do. Moreover, this avoids
184 freeing and reallocating a QBindingStatus instance.
185
186 Note that the lifetime is coupled to the QThreadData, which is kept alive by QObjects, even if
187 the corresponding QThread is gone.
188
189 The draw-back here is that even plain QProperty will cause the creation of QThreadData if used
190 in a thread which doesn't already have it; however that should be rare in practice.
191 */
192
193
194Q_CONSTINIT thread_local QBindingStatus *tl_status = nullptr;
195
196Q_NEVER_INLINE static void initBindingStatus()
197{
198 auto status = new QBindingStatus {};
199 /* needs to happen before setting it on the thread-data, as
200 setStatusAndClearList might need to actually update the objectt list, which would
201 end up calling into bindingStatus again
202 */
203 tl_status = status;
204 QThreadData *threadData = QThreadData::current();
205 QThread *currentThread = threadData->thread;
206 if (currentThread) {
207 QThreadPrivate *threadPriv = static_cast<QThreadPrivate *>(QObjectPrivate::get(o: currentThread));
208 QMutexLocker lock(&threadPriv->mutex);
209 threadData->m_statusOrPendingObjects.setStatusAndClearList(status);
210 } else {
211 // if QThreadData is in the process of being created, we don't need to synchronize, as there's
212 // no QThread to which another thread could move objects to
213 threadData->m_statusOrPendingObjects.setStatusAndClearList(status);
214 }
215}
216
217static QBindingStatus &bindingStatus()
218{
219 if (!tl_status)
220 initBindingStatus();
221 return *tl_status;
222}
223
224/*!
225 \since 6.2
226
227 \relates QProperty
228
229 Marks the beginning of a property update group. Inside this group,
230 changing a property does neither immediately update any dependent properties
231 nor does it trigger change notifications.
232 Those are instead deferred until the group is ended by a call to endPropertyUpdateGroup.
233
234 Groups can be nested. In that case, the deferral ends only after the outermost group has been
235 ended.
236
237 \note Change notifications are only send after all property values affected by the group have
238 been updated to their new values. This allows re-establishing a class invariant if multiple
239 properties need to be updated, preventing any external observer from noticing an inconsistent
240 state.
241
242 \sa Qt::endPropertyUpdateGroup, QScopedPropertyUpdateGroup
243*/
244void Qt::beginPropertyUpdateGroup()
245{
246 QPropertyDelayedNotifications *& groupUpdateData = bindingStatus().groupUpdateData;
247 if (!groupUpdateData)
248 groupUpdateData = new QPropertyDelayedNotifications;
249 ++groupUpdateData->ref;
250}
251
252/*!
253 \since 6.2
254 \relates QProperty
255
256 Ends a property update group. If the outermost group has been ended, and deferred
257 binding evaluations and notifications happen now.
258
259 \warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
260 results in undefined behavior.
261
262 \sa Qt::beginPropertyUpdateGroup, QScopedPropertyUpdateGroup
263*/
264void Qt::endPropertyUpdateGroup()
265{
266 auto status = &bindingStatus();
267 QPropertyDelayedNotifications *& groupUpdateData = status->groupUpdateData;
268 auto *data = groupUpdateData;
269 Q_ASSERT(data->ref);
270 if (--data->ref)
271 return;
272 groupUpdateData = nullptr;
273 // ensures that bindings are kept alive until endPropertyUpdateGroup concludes
274 PendingBindingObserverList bindingObservers;
275 // update all delayed properties
276 auto start = data;
277 while (data) {
278 for (qsizetype i = 0; i < data->used; ++i)
279 data->evaluateBindings(bindingObservers, index: i, status);
280 data = data->next;
281 }
282 // notify all delayed notifications from binding evaluation
283 for (const auto &bindingPtr: bindingObservers) {
284 auto *binding = static_cast<QPropertyBindingPrivate *>(bindingPtr.get());
285 binding->notifyNonRecursive();
286 }
287 // do the same for properties which only have observers
288 data = start;
289 while (data) {
290 for (qsizetype i = 0; i < data->used; ++i)
291 data->notify(index: i);
292 delete std::exchange(obj&: data, new_val&: data->next);
293 }
294}
295
296/*!
297 \since 6.6
298 \class QScopedPropertyUpdateGroup
299 \inmodule QtCore
300 \ingroup tools
301 \brief RAII class around Qt::beginPropertyUpdateGroup()/Qt::endPropertyUpdateGroup().
302
303 This class calls Qt::beginPropertyUpdateGroup() in its constructor and
304 Qt::endPropertyUpdateGroup() in its destructor, making sure the latter
305 function is reliably called even in the presence of early returns or thrown
306 exceptions.
307
308 \note Qt::endPropertyUpdateGroup() may re-throw exceptions thrown by
309 binding evaluations. This means your application may crash
310 (\c{std::terminate()} called) if another exception is causing
311 QScopedPropertyUpdateGroup's destructor to be called during stack
312 unwinding. If you expect exceptions from binding evaluations, use manual
313 Qt::endPropertyUpdateGroup() calls and \c{try}/\c{catch} blocks.
314
315 \sa QProperty
316*/
317
318/*!
319 \fn QScopedPropertyUpdateGroup::QScopedPropertyUpdateGroup()
320
321 Calls Qt::beginPropertyUpdateGroup().
322*/
323
324/*!
325 \fn QScopedPropertyUpdateGroup::~QScopedPropertyUpdateGroup()
326
327 Calls Qt::endPropertyUpdateGroup().
328*/
329
330
331// check everything stored in QPropertyBindingPrivate's union is trivially destructible
332// (though the compiler would also complain if that weren't the case)
333static_assert(std::is_trivially_destructible_v<QPropertyBindingSourceLocation>);
334static_assert(std::is_trivially_destructible_v<std::byte[sizeof(QPropertyBindingSourceLocation)]>);
335
336QPropertyBindingPrivate::~QPropertyBindingPrivate()
337{
338 if (firstObserver)
339 firstObserver.unlink();
340 if (vtable->size)
341 vtable->destroy(reinterpret_cast<std::byte *>(this)
342 + QPropertyBindingPrivate::getSizeEnsuringAlignment());
343}
344
345void QPropertyBindingPrivate::clearDependencyObservers() {
346 for (size_t i = 0; i < qMin(a: dependencyObserverCount, b: inlineDependencyObservers.size()); ++i) {
347 QPropertyObserverPointer p{.ptr: &inlineDependencyObservers[i]};
348 p.unlink_fast();
349 }
350 if (heapObservers)
351 heapObservers->clear();
352 dependencyObserverCount = 0;
353}
354
355QPropertyObserverPointer QPropertyBindingPrivate::allocateDependencyObserver_slow()
356{
357 ++dependencyObserverCount;
358 if (!heapObservers)
359 heapObservers.reset(p: new std::vector<QPropertyObserver>());
360 return {.ptr: &heapObservers->emplace_back()};
361}
362
363void QPropertyBindingPrivate::unlinkAndDeref()
364{
365 clearDependencyObservers();
366 propertyDataPtr = nullptr;
367 if (!deref())
368 destroyAndFreeMemory(priv: this);
369}
370
371bool QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
372{
373 if (!status)
374 status = &bindingStatus();
375 return evaluateRecursive_inline(bindingObservers, status);
376}
377
378void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
379{
380 notifyNonRecursive();
381 for (auto &&bindingPtr: bindingObservers) {
382 auto *binding = static_cast<QPropertyBindingPrivate *>(bindingPtr.get());
383 binding->notifyNonRecursive();
384 }
385}
386
387QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRecursive()
388{
389 if (!pendingNotify)
390 return Delayed;
391 pendingNotify = false;
392 Q_ASSERT(!updating);
393 updating = true;
394 if (firstObserver) {
395 firstObserver.noSelfDependencies(binding: this);
396 firstObserver.notify(propertyDataPtr);
397 }
398 if (hasStaticObserver)
399 staticObserverCallback(propertyDataPtr);
400 updating = false;
401 return Sent;
402}
403
404/*!
405 Constructs a null QUntypedPropertyBinding.
406
407 \sa isNull()
408*/
409QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
410
411/*!
412 \fn template<typename Functor>
413 QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location)
414
415 \internal
416*/
417
418/*!
419 \internal
420
421 Constructs QUntypedPropertyBinding. Assumes that \a metaType, \a function and \a vtable match.
422 Unless a specialization of \c BindingFunctionVTable is used, this function should never be called
423 directly.
424*/
425QUntypedPropertyBinding::QUntypedPropertyBinding(QMetaType metaType, const BindingFunctionVTable *vtable, void *function,
426 const QPropertyBindingSourceLocation &location)
427{
428 std::byte *mem = new std::byte[QPropertyBindingPrivate::getSizeEnsuringAlignment() + vtable->size]();
429 d = new(mem) QPropertyBindingPrivate(metaType, vtable, std::move(location));
430 vtable->moveConstruct(mem + QPropertyBindingPrivate::getSizeEnsuringAlignment(), function);
431}
432
433/*!
434 Move-constructs a QUntypedPropertyBinding from \a other.
435
436 \a other is left in a null state.
437 \sa isNull()
438*/
439QUntypedPropertyBinding::QUntypedPropertyBinding(QUntypedPropertyBinding &&other)
440 : d(std::move(other.d))
441{
442}
443
444/*!
445 Copy-constructs a QUntypedPropertyBinding from \a other.
446*/
447QUntypedPropertyBinding::QUntypedPropertyBinding(const QUntypedPropertyBinding &other)
448 : d(other.d)
449{
450}
451
452/*!
453 Copy-assigns \a other to this QUntypedPropertyBinding.
454*/
455QUntypedPropertyBinding &QUntypedPropertyBinding::operator=(const QUntypedPropertyBinding &other)
456{
457 d = other.d;
458 return *this;
459}
460
461/*!
462 Move-assigns \a other to this QUntypedPropertyBinding.
463
464 \a other is left in a null state.
465 \sa isNull
466*/
467QUntypedPropertyBinding &QUntypedPropertyBinding::operator=(QUntypedPropertyBinding &&other)
468{
469 d = std::move(other.d);
470 return *this;
471}
472
473/*!
474 \internal
475*/
476QUntypedPropertyBinding::QUntypedPropertyBinding(QPropertyBindingPrivate *priv)
477 : d(priv)
478{
479}
480
481/*!
482 Destroys the QUntypedPropertyBinding.
483*/
484QUntypedPropertyBinding::~QUntypedPropertyBinding()
485{
486}
487
488/*!
489 Returns \c true if the \c QUntypedPropertyBinding is null.
490 This is only true for default-constructed and moved-from instances.
491
492 \sa isNull()
493*/
494bool QUntypedPropertyBinding::isNull() const
495{
496 return !d;
497}
498
499/*!
500 Returns the error state of the binding.
501
502 \sa QPropertyBindingError
503*/
504QPropertyBindingError QUntypedPropertyBinding::error() const
505{
506 if (!d)
507 return QPropertyBindingError();
508 return static_cast<QPropertyBindingPrivate *>(d.get())->bindingError();
509}
510
511/*!
512 Returns the meta-type of the binding.
513 If the QUntypedPropertyBinding is null, an invalid QMetaType is returned.
514*/
515QMetaType QUntypedPropertyBinding::valueMetaType() const
516{
517 if (!d)
518 return QMetaType();
519 return static_cast<QPropertyBindingPrivate *>(d.get())->valueMetaType();
520}
521
522QPropertyBindingData::~QPropertyBindingData()
523{
524 QPropertyBindingDataPointer d{.ptr: this};
525 if (isNotificationDelayed())
526 proxyData()->originalBindingData = nullptr;
527 for (auto observer = d.firstObserver(); observer;) {
528 auto next = observer.nextObserver();
529 observer.unlink();
530 observer = next;
531 }
532 if (auto binding = d.binding())
533 binding->unlinkAndDeref();
534}
535
536QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyBinding &binding,
537 QUntypedPropertyData *propertyDataPtr,
538 QPropertyObserverCallback staticObserverCallback,
539 QtPrivate::QPropertyBindingWrapper guardCallback)
540{
541 QPropertyBindingPrivatePtr oldBinding;
542 QPropertyBindingPrivatePtr newBinding = binding.d;
543
544 QPropertyBindingDataPointer d{.ptr: this};
545 QPropertyObserverPointer observer;
546
547 auto &data = d_ref();
548 if (auto *existingBinding = d.binding()) {
549 if (existingBinding == newBinding.data())
550 return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
551 if (existingBinding->isUpdating()) {
552 existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
553 return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
554 }
555 oldBinding = QPropertyBindingPrivatePtr(existingBinding);
556 observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
557 static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
558 data = 0;
559 } else {
560 observer = d.firstObserver();
561 }
562
563 if (newBinding) {
564 newBinding.data()->addRef();
565 data = reinterpret_cast<quintptr>(newBinding.data());
566 data |= BindingBit;
567 auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
568 newBindingRaw->setProperty(propertyDataPtr);
569 if (observer)
570 newBindingRaw->prependObserver(observer);
571 newBindingRaw->setStaticObserver(callback: staticObserverCallback, bindingWrapper: guardCallback);
572
573 PendingBindingObserverList bindingObservers;
574 newBindingRaw->evaluateRecursive(bindingObservers);
575 newBindingRaw->notifyNonRecursive(bindingObservers);
576 } else if (observer) {
577 d.setObservers(observer.ptr);
578 } else {
579 data = 0;
580 }
581
582 if (oldBinding)
583 static_cast<QPropertyBindingPrivate *>(oldBinding.data())->detachFromProperty();
584
585 return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
586}
587
588QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr(std::exchange(obj&: other.d_ptr, new_val: 0))
589{
590 QPropertyBindingDataPointer::fixupAfterMove(ptr: this);
591}
592
593BindingEvaluationState::BindingEvaluationState(QPropertyBindingPrivate *binding, QBindingStatus *status)
594 : binding(binding)
595{
596 Q_ASSERT(status);
597 QBindingStatus *s = status;
598 // store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in
599 // the destructor (as these come with a non zero cost)
600 currentState = &s->currentlyEvaluatingBinding;
601 previousState = *currentState;
602 *currentState = this;
603 binding->clearDependencyObservers();
604}
605
606CompatPropertySafePoint::CompatPropertySafePoint(QBindingStatus *status, QUntypedPropertyData *property)
607 : property(property)
608{
609 // store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in
610 // the destructor (as these come with a non zero cost)
611 currentState = &status->currentCompatProperty;
612 previousState = *currentState;
613 *currentState = this;
614
615 currentlyEvaluatingBindingList = &bindingStatus().currentlyEvaluatingBinding;
616 bindingState = *currentlyEvaluatingBindingList;
617 *currentlyEvaluatingBindingList = nullptr;
618}
619
620QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding()
621{
622 auto currentState = bindingStatus().currentlyEvaluatingBinding ;
623 return currentState ? currentState->binding : nullptr;
624}
625
626// ### Unused, kept for BC with 6.0
627void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *) const
628{
629}
630
631void QPropertyBindingData::removeBinding_helper()
632{
633 QPropertyBindingDataPointer d{.ptr: this};
634
635 auto *existingBinding = d.binding();
636 Q_ASSERT(existingBinding);
637 if (existingBinding->isSticky()) {
638 return;
639 }
640
641 auto observer = existingBinding->takeObservers();
642 d_ref() = 0;
643 if (observer)
644 d.setObservers(observer.ptr);
645 existingBinding->unlinkAndDeref();
646}
647
648void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding() const
649{
650 auto currentState = bindingStatus().currentlyEvaluatingBinding;
651 if (!currentState)
652 return;
653 registerWithCurrentlyEvaluatingBinding_helper(currentBinding: currentState);
654}
655
656
657void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentState) const
658{
659 QPropertyBindingDataPointer d{.ptr: this};
660
661 if (currentState->alreadyCaptureProperties.contains(t: this))
662 return;
663 else
664 currentState->alreadyCaptureProperties.push_back(t: this);
665
666 QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
667 Q_ASSERT(QPropertyObserver::ObserverNotifiesBinding == 0);
668 dependencyObserver.setBindingToNotify_unsafe(currentState->binding);
669 d.addObserver(observer: dependencyObserver.ptr);
670}
671
672void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr) const
673{
674 notifyObservers(propertyDataPtr, storage: nullptr);
675}
676
677void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage) const
678{
679 if (isNotificationDelayed())
680 return;
681 QPropertyBindingDataPointer d{.ptr: this};
682
683 PendingBindingObserverList bindingObservers;
684 if (QPropertyObserverPointer observer = d.firstObserver()) {
685 if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
686 /* evaluateBindings() can trash the observers. We need to re-fetch here.
687 "this" might also no longer be valid in case we have a QObjectBindableProperty
688 and consequently d isn't either (this happens when binding evaluation has
689 caused the binding storage to resize.
690 If storage is nullptr, then there is no dynamically resizable storage,
691 and we cannot run into the issue.
692 */
693 if (storage)
694 d = QPropertyBindingDataPointer {.ptr: storage->bindingData(data: propertyDataPtr)};
695 if (QPropertyObserverPointer observer = d.firstObserver())
696 observer.notify(propertyDataPtr);
697 for (auto &&bindingPtr: bindingObservers) {
698 auto *binding = static_cast<QPropertyBindingPrivate *>(bindingPtr.get());
699 binding->notifyNonRecursive();
700 }
701 }
702 }
703}
704
705QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper
706(
707 QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
708 QPropertyObserverPointer observer,
709 PendingBindingObserverList &bindingObservers) const
710{
711#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
712 QBindingStatus *status = storage ? storage->bindingStatus : nullptr;
713 if (!status || status->threadId != QThread::currentThreadId())
714 status = &bindingStatus();
715#else
716 Q_UNUSED(storage);
717 QBindingStatus *status = &bindingStatus();
718#endif
719 if (QPropertyDelayedNotifications *delay = status->groupUpdateData) {
720 delay->addProperty(bindingData: this, propertyData: propertyDataPtr);
721 return Delayed;
722 }
723
724 observer.evaluateBindings(bindingObservers, status);
725 return Evaluated;
726}
727
728
729QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
730{
731 QPropertyObserverPointer d{.ptr: this};
732 d.setChangeHandler(changeHandler);
733}
734
735#if QT_DEPRECATED_SINCE(6, 6)
736QPropertyObserver::QPropertyObserver(QUntypedPropertyData *data)
737{
738 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
739 aliasData = data;
740 next.setTag(ObserverIsAlias);
741 QT_WARNING_POP
742}
743#endif
744
745/*! \internal
746*/
747void QPropertyObserver::setSource(const QPropertyBindingData &property)
748{
749 QPropertyObserverPointer d{.ptr: this};
750 QPropertyBindingDataPointer propPrivate{.ptr: &property};
751 d.observeProperty(property: propPrivate);
752}
753
754QPropertyObserver::~QPropertyObserver()
755{
756 QPropertyObserverPointer d{.ptr: this};
757 d.unlink();
758}
759
760QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
761{
762 binding = std::exchange(obj&: other.binding, new_val: {});
763 next = std::exchange(obj&: other.next, new_val: {});
764 prev = std::exchange(obj&: other.prev, new_val: {});
765 if (next)
766 next->prev = &next;
767 if (prev)
768 prev.setPointer(this);
769}
770
771QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexcept
772{
773 if (this == &other)
774 return *this;
775
776 QPropertyObserverPointer d{.ptr: this};
777 d.unlink();
778 binding = nullptr;
779
780 binding = std::exchange(obj&: other.binding, new_val: {});
781 next = std::exchange(obj&: other.next, new_val: {});
782 prev = std::exchange(obj&: other.prev, new_val: {});
783 if (next)
784 next->prev = &next;
785 if (prev)
786 prev.setPointer(this);
787
788 return *this;
789}
790
791/*!
792 \fn QPropertyObserverPointer::unlink()
793 \internal
794 Unlinks
795 */
796
797
798/*!
799 \fn QPropertyObserverPointer::unlink_fast()
800 \internal
801 Like unlink, but does not handle ObserverIsAlias.
802 Must only be called in places where we know that we are not dealing
803 with such an observer.
804 */
805
806void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler changeHandler)
807{
808 Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
809 ptr->changeHandler = changeHandler;
810 ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler);
811}
812
813/*!
814 \internal
815 The same as setBindingToNotify, but assumes that the tag is already correct.
816 */
817void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
818{
819 Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
820 ptr->binding = binding;
821}
822
823/*!
824 \class QPropertyObserverNodeProtector
825 \internal
826 QPropertyObserverNodeProtector is a RAII wrapper which takes care of the internal switching logic
827 for QPropertyObserverPointer::notify (described ibidem)
828*/
829
830/*!
831 \fn QPropertyObserverNodeProtector::notify(QUntypedPropertyData *propertyDataPtr)
832 \internal
833 \a propertyDataPtr is a pointer to the observed property's property data
834*/
835
836#ifndef QT_NO_DEBUG
837void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *binding)
838{
839 auto observer = const_cast<QPropertyObserver*>(ptr);
840 // See also comment in notify()
841 while (observer) {
842 if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding)
843 if (observer->binding == binding) {
844 qCritical(msg: "Property depends on itself!");
845 break;
846 }
847
848 observer = observer->next.data();
849 }
850
851}
852#endif
853
854void QPropertyObserverPointer::evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
855{
856 Q_ASSERT(status);
857 auto observer = const_cast<QPropertyObserver*>(ptr);
858 // See also comment in notify()
859 while (observer) {
860 QPropertyObserver *next = observer->next.data();
861
862 if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
863 auto bindingToEvaluate = observer->binding;
864 QPropertyObserverNodeProtector protector(observer);
865 // binding must not be gone after evaluateRecursive_inline
866 QPropertyBindingPrivatePtr currentBinding(observer->binding);
867 const bool evalStatus = bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status);
868 if (evalStatus)
869 bindingObservers.push_back(t: std::move(currentBinding));
870 next = protector.next();
871 }
872
873 observer = next;
874 }
875}
876
877void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer property)
878{
879 if (ptr->prev)
880 unlink();
881 property.addObserver(observer: ptr);
882}
883
884/*!
885 \class QPropertyBindingError
886 \inmodule QtCore
887 \ingroup tools
888 \since 6.0
889
890 QPropertyBindingError is used by \l{The Property System}{the property
891 system} to report errors that occurred when a binding was evaluated. Use \l
892 type() to query which error occurred, and \l
893 description() to extract an error message which might contain
894 more details.
895 If there is no error, QPropertyBindingError has type
896 \c QPropertyBindingError::NoError and \c hasError() returns false.
897
898 \code
899 extern QProperty<int> prop;
900
901 QPropertyBindingError error = prop.binding().error();
902 if (error.hasError())
903 qDebug() << error.description();
904 \endcode
905*/
906
907/*!
908 \enum QPropertyBindingError::Type
909
910 This enum specifies which error occurred.
911
912 \value NoError
913 No error occurred while evaluating the binding.
914 \value BindingLoop
915 Binding evaluation was stopped because a property depended on its own
916 value.
917 \value EvaluationError
918 Binding evaluation was stopped for any other reason than a binding loop.
919 For example, this value is used in the QML engine when an exception occurs
920 while a binding is evaluated.
921 \value UnknownError
922 A generic error type used when neither of the other values is suitable.
923 Calling \l description() might provide details.
924*/
925
926/*!
927 Default constructs QPropertyBindingError.
928 hasError() will return false, type will return \c NoError and
929 \l description() will return an empty string.
930*/
931QPropertyBindingError::QPropertyBindingError()
932{
933}
934
935/*!
936 Constructs a QPropertyBindingError of type \a type with \a description as its
937 description.
938*/
939QPropertyBindingError::QPropertyBindingError(Type type, const QString &description)
940{
941 if (type != NoError) {
942 d = new QPropertyBindingErrorPrivate;
943 d->type = type;
944 d->description = description;
945 }
946}
947
948/*!
949 Copy-constructs QPropertyBindingError from \a other.
950*/
951QPropertyBindingError::QPropertyBindingError(const QPropertyBindingError &other)
952 : d(other.d)
953{
954}
955
956/*!
957 Copies \a other to this QPropertyBindingError.
958*/
959QPropertyBindingError &QPropertyBindingError::operator=(const QPropertyBindingError &other)
960{
961 d = other.d;
962 return *this;
963}
964
965/*!
966 Move-constructs QPropertyBindingError from \a other.
967 \a other will be left in its default state.
968*/
969QPropertyBindingError::QPropertyBindingError(QPropertyBindingError &&other)
970 : d(std::move(other.d))
971{
972}
973
974/*!
975 Move-assigns \a other to this QPropertyBindingError.
976 \a other will be left in its default state.
977*/
978QPropertyBindingError &QPropertyBindingError::operator=(QPropertyBindingError &&other)
979{
980 d = std::move(other.d);
981 return *this;
982}
983
984/*!
985 Destroys the QPropertyBindingError.
986*/
987QPropertyBindingError::~QPropertyBindingError()
988{
989}
990
991/*!
992 Returns the type of the QPropertyBindingError.
993
994 \sa QPropertyBindingError::Type
995*/
996QPropertyBindingError::Type QPropertyBindingError::type() const
997{
998 if (!d)
999 return QPropertyBindingError::NoError;
1000 return d->type;
1001}
1002
1003/*!
1004 Returns a descriptive error message for the QPropertyBindingError if
1005 it has been set.
1006*/
1007QString QPropertyBindingError::description() const
1008{
1009 if (!d)
1010 return QString();
1011 return d->description;
1012}
1013
1014/*!
1015 \class QPropertyData
1016 \inmodule QtCore
1017 \brief The QPropertyData class is a helper class for properties with automatic property bindings.
1018 \since 6.0
1019
1020 \ingroup tools
1021
1022 QPropertyData\<T\> is a common base class for classes that can hold properties with automatic
1023 data bindings. It mainly wraps the stored data, and offers low level access to that data.
1024
1025 The low level access to the data provided by this class bypasses the binding mechanism, and should be
1026 used with care, as updates to the values will not get propagated to any bindings that depend on this
1027 property.
1028
1029 You should usually call value() and setValue() on QProperty<T> or QObjectBindableProperty<T>, not use
1030 the low level mechanisms provided in this class.
1031*/
1032
1033/*! \fn template <typename T> QPropertyData<T>::parameter_type QPropertyData<T>::valueBypassingBindings() const
1034
1035 Returns the data stored in this property.
1036
1037 \note As this will bypass any binding evaluation it might return an outdated value if a
1038 binding is set on this property. Using this method will also not register the property
1039 access with any currently executing binding.
1040*/
1041
1042/*! \fn template <typename T> void QPropertyData<T>::setValueBypassingBindings(parameter_type v)
1043
1044 Sets the data value stored in this property to \a v.
1045
1046 \note Using this method will bypass any potential binding registered for this property.
1047*/
1048
1049/*! \fn template <typename T> void QPropertyData<T>::setValueBypassingBindings(rvalue_ref v)
1050 \overload
1051
1052 Sets the data value stored in this property to \a v.
1053
1054 \note Using this method will bypass any potential binding registered for this property.
1055*/
1056
1057/*!
1058 \class QUntypedBindable
1059 \inmodule QtCore
1060 \brief QUntypedBindable is a uniform interface over bindable properties like \c QProperty\<T\>
1061 and \c QObjectBindableProperty of any type \c T.
1062 \since 6.0
1063
1064 \ingroup tools
1065
1066 QUntypedBindable is a fully type-erased generic interface to wrap bindable properties.
1067 You can use it to interact with properties without knowing their type nor caring what
1068 kind of bindable property they are (e.g. QProperty or QObjectBindableProperty).
1069 For most use cases, using QBindable\<T\> (which is generic over the property implementation
1070 but has a fixed type) should be preferred.
1071*/
1072
1073/*!
1074 \fn QUntypedBindable::QUntypedBindable()
1075
1076 Default-constructs a QUntypedBindable. It is in an invalid state.
1077 \sa isValid()
1078*/
1079
1080/*!
1081 \fn template<typename Property> QUntypedBindable::QUntypedBindable(Property *property)
1082
1083 Constructs a QUntypedBindable from the property \a property. If Property is const,
1084 the QUntypedBindable will be read only. If \a property is null, the QUntypedBindable
1085 will be invalid.
1086
1087 \sa isValid(), isReadOnly()
1088*/
1089
1090/*!
1091 \fn bool QUntypedBindable::isValid() const
1092
1093 Returns true if the QUntypedBindable is valid. Methods called on an invalid
1094 QUntypedBindable generally have no effect, unless otherwise noted.
1095*/
1096
1097/*!
1098 \fn bool QUntypedBindable::isReadOnly() const
1099 \since 6.1
1100
1101 Returns true if the QUntypedBindable is read-only.
1102*/
1103
1104/*!
1105 \fn bool QUntypedBindable::isBindable() const
1106 \internal
1107
1108 Returns true if the underlying property's binding can be queried
1109 with binding() and, if not read-only, changed with setBinding.
1110 Only QObjectComputedProperty currently leads to this method returning
1111 false.
1112
1113 \sa isReadOnly()
1114*/
1115
1116/*!
1117 \fn QUntypedPropertyBinding QUntypedBindable::makeBinding(const QPropertyBindingSourceLocation &location) const
1118
1119 Creates a binding returning the underlying properties' value, using a specified source \a location.
1120*/
1121
1122/*!
1123 \fn void QUntypedBindable::observe(QPropertyObserver *observer)
1124 \internal
1125
1126 Installs the observer on the underlying property.
1127*/
1128
1129/*!
1130 \fn template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::onValueChanged(Functor f) const
1131
1132 Installs \a f as a change handler. Whenever the underlying property changes, \a f will be called, as
1133 long as the returned \c QPropertyChangeHandler and the property are kept alive.
1134 On each value change, the handler is either called immediately, or deferred, depending on the context.
1135
1136 \sa QProperty::onValueChanged(), subscribe()
1137*/
1138
1139/*!
1140 \fn template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f) const
1141
1142 Behaves like a call to \a f followed by \c onValueChanged(f),
1143
1144 \sa onValueChanged()
1145*/
1146
1147/*!
1148 \fn template<typename Functor> QPropertyNotifier QUntypedBindable::addNotifier(Functor f)
1149
1150 Installs \a f as a change handler. Whenever the underlying property changes, \a f will be called, as
1151 long as the returned \c QPropertyNotifier and the property are kept alive.
1152
1153 This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
1154 It can therefore more easily be stored, e.g. as a member in a class.
1155
1156 \sa onValueChanged(), subscribe()
1157*/
1158
1159/*!
1160 \fn QUntypedPropertyBinding QUntypedBindable::binding() const
1161
1162 Returns the underlying property's binding if there is any, or a default
1163 constructed QUntypedPropertyBinding otherwise.
1164
1165 \sa hasBinding()
1166*/
1167
1168/*!
1169 \fn QUntypedPropertyBinding QUntypedBindable::takeBinding()
1170
1171 Removes the currently set binding from the property and returns it.
1172 Returns a default-constructed QUntypedPropertyBinding if no binding is set.
1173
1174 \since 6.1
1175*/
1176
1177/*!
1178 \fn bool QUntypedBindable::setBinding(const QUntypedPropertyBinding &binding)
1179
1180 Sets the underlying property's binding to \a binding. This does not have any effect
1181 if the QUntypedBindable is read-only, null or if \a binding's type does match the
1182 underlying property's type.
1183
1184 \return \c true when the binding was successfully set.
1185
1186 //! \sa QUntypedPropertyBinding::valueMetaType()
1187*/
1188
1189/*!
1190 \fn bool QUntypedBindable::hasBinding() const
1191
1192 Returns \c true if the underlying property has a binding.
1193*/
1194
1195/*!
1196 \fn QMetaType QUntypedBindable::metaType() const
1197 \since 6.2
1198
1199 Returns the metatype of the property from which the QUntypedBindable was created.
1200 If the bindable is invalid, an invalid metatype will be returned.
1201
1202 \sa isValid()
1203 //! \sa QUntypedPropertyBinding::valueMetaType()
1204*/
1205
1206/*!
1207 \class QBindable
1208 \inmodule QtCore
1209 \brief QBindable is a wrapper class around binding-enabled properties. It allows type-safe
1210 operations while abstracting the differences between the various property classes away.
1211 \inherits QUntypedBindable
1212
1213 \ingroup tools
1214
1215 QBindable\<T\> helps to integrate Qt's traditional Q_PROPERTY with
1216 \l {Qt Bindable Properties}{binding-enabled} properties.
1217 If a property is backed by a QProperty, QObjectBindableProperty or QObjectComputedProperty,
1218 you can add \c BINDABLE bindablePropertyName to the Q_PROPERTY
1219 declaration, where bindablePropertyName is a function returning an instance of QBindable
1220 constructed from the QProperty. The returned QBindable allows users of the property to set
1221 and query bindings of the property, without having to know the exact kind of binding-enabled
1222 property used.
1223
1224 \snippet code/src_corelib_kernel_qproperty.cpp 0
1225 \snippet code/src_corelib_kernel_qproperty.cpp 3
1226
1227 \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty,
1228 QObjectComputedProperty, {Qt Bindable Properties}
1229*/
1230
1231/*!
1232 \fn template<typename T> QBindable<T>::QBindable(QObject *obj, const char *property)
1233 \since 6.5
1234
1235 Constructs a QBindable for the \l Q_PROPERTY \a property on \a obj. The property must
1236 have a notify signal but does not need to have \c BINDABLE in its \c Q_PROPERTY
1237 definition, so even binding unaware \c {Q_PROPERTY}s can be bound or used in binding
1238 expressions. You must use \c QBindable::value() in binding expressions instead of the
1239 normal property \c READ function (or \c MEMBER) to enable dependency tracking if the
1240 property is not \c BINDABLE. When binding using a lambda, you may prefer to capture the
1241 QBindable by value to avoid the cost of calling this constructor in the binding
1242 expression.
1243 This constructor should not be used to implement \c BINDABLE for a Q_PROPERTY, as the
1244 resulting Q_PROPERTY will not support dependency tracking. To make a property that is
1245 usable directly without reading through a QBindable use \l QProperty or
1246 \l QObjectBindableProperty.
1247
1248 \code
1249 QProperty<QString> displayText;
1250 QDateTimeEdit *dateTimeEdit = findDateTimeEdit();
1251 QBindable<QDateTime> dateTimeBindable(dateTimeEdit, "dateTime");
1252 displayText.setBinding([dateTimeBindable](){ return dateTimeBindable.value().toString(); });
1253 \endcode
1254
1255 \sa QProperty, QObjectBindableProperty, {Qt Bindable Properties}
1256*/
1257
1258/*!
1259 \fn template<typename T> QBindable<T>::QBindable(QObject *obj, const QMetaProperty &property)
1260 \since 6.5
1261
1262 See \l QBindable::QBindable(QObject *obj, const char *property)
1263*/
1264
1265/*!
1266 \fn template<typename T> QPropertyBinding<T> QBindable<T>::makeBinding(const QPropertyBindingSourceLocation &location) const
1267
1268 Constructs a binding evaluating to the underlying property's value, using a specified source
1269 \a location.
1270*/
1271
1272/*!
1273 \fn template <typename T> QPropertyBinding<T> QBindable<T>::binding() const
1274
1275 Returns the currently set binding of the underlying property. If the property does not
1276 have a binding, the returned \c QPropertyBinding<T> will be invalid.
1277
1278 \sa setBinding, hasBinding
1279 //! \sa QPropertyBinding::isValid()
1280*/
1281
1282/*!
1283 \fn template <typename T> QPropertyBinding<T> QBindable<T>::takeBinding()
1284
1285 Removes the currently set binding of the underlying property and returns it.
1286 If the property does not have a binding, the returned \c QPropertyBinding<T> will be invalid.
1287
1288 \sa binding, setBinding, hasBinding
1289 //! \sa QPropertyBinding::isValid()
1290*/
1291
1292
1293/*!
1294 \fn template <typename T> void QBindable<T>::setBinding(const QPropertyBinding<T> &binding)
1295
1296 Sets the underlying property's binding to \a binding. Does nothing if the QBindable is
1297 read-only or invalid.
1298
1299 \sa binding, isReadOnly(), isValid()
1300 //! \sa QPropertyBinding::isValid()
1301*/
1302
1303/*!
1304 \fn template <typename T> template <typename Functor> QPropertyBinding<T> QBindable<T>::setBinding(Functor f);
1305 \overload
1306
1307 Creates a \c QPropertyBinding<T> from \a f, and sets it as the underlying property's binding.
1308*/
1309
1310/*!
1311 \fn template <typename T> T QBindable<T>::value() const
1312
1313 Returns the underlying property's current value. If the QBindable is invalid,
1314 a default constructed \c T is returned.
1315
1316 \sa isValid()
1317*/
1318
1319/*!
1320 \fn template <typename T> void QBindable<T>::setValue(const T &value)
1321
1322 Sets the underlying property's value to \a value. This removes any currenltly set
1323 binding from it. This function has no effect if the QBindable is read-only or invalid.
1324
1325 \sa isValid(), isReadOnly(), setBinding()
1326*/
1327
1328/*!
1329 \class QProperty
1330 \inmodule QtCore
1331 \brief The QProperty class is a template class that enables automatic property bindings.
1332 \since 6.0
1333 \compares equality
1334 \compareswith equality T
1335 \endcompareswith
1336
1337 \ingroup tools
1338
1339 QProperty\<T\> is one of the classes implementing \l {Qt Bindable Properties}.
1340 It is a container that holds an instance of T. You can assign
1341 a value to it and you can read it via the value() function or the T conversion
1342 operator. You can also tie the property to an expression that computes the value
1343 dynamically, the binding expression. It is represented as a C++ lambda and
1344 can be used to express relationships between different properties in your
1345 application.
1346
1347 \note For QML, it's important to expose the \l QProperty in \l Q_PROPERTY
1348 with the BINDABLE keyword. As a result, the QML engine uses
1349 it as the bindable interface to set up the property binding. In turn, the
1350 binding can then be interacted with C++ via the normal API:
1351 QProperty<T>::onValueChanged, QProperty::takeBinding and QBindable::hasBinding
1352 If the property is BINDABLE, the engine will use the change-tracking
1353 inherent to the C++ property system for getting notified about changes, and it
1354 won't rely on signals being emitted.
1355*/
1356
1357/*!
1358 \fn template <typename T> QProperty<T>::QProperty()
1359
1360 Constructs a property with a default constructed instance of T.
1361*/
1362
1363/*!
1364 \fn template <typename T> explicit QProperty<T>::QProperty(const T &initialValue)
1365
1366 Constructs a property with the provided \a initialValue.
1367*/
1368
1369/*!
1370 \fn template <typename T> explicit QProperty<T>::QProperty(T &&initialValue)
1371
1372 Move-Constructs a property with the provided \a initialValue.
1373*/
1374
1375/*!
1376 \fn template <typename T> QProperty<T>::QProperty(QProperty<T> &&other)
1377
1378 Move-constructs a QProperty instance, making it point at the same object that
1379 \a other was pointing to.
1380*/
1381
1382/*!
1383 \fn template <typename T> QProperty<T>::QProperty(const QPropertyBinding<T> &binding)
1384
1385 Constructs a property that is tied to the provided \a binding expression.
1386 The property's value is set to the result of evaluating the new binding.
1387 Whenever a dependency of the binding changes, the binding will be re-evaluated,
1388 and the property's value gets updated accordingly.
1389*/
1390
1391/*!
1392 \fn template <typename T> template <typename Functor> QProperty<T>::QProperty(Functor &&f)
1393
1394 Constructs a property that is tied to the provided binding expression \a f.
1395 The property's value is set to the result of evaluating the new binding.
1396 Whenever a dependency of the binding changes, the binding will be re-evaluated,
1397 and the property's value gets updated accordingly.
1398 */
1399
1400/*!
1401 \fn template <typename T> QProperty<T>::~QProperty()
1402
1403 Destroys the property.
1404*/
1405
1406/*!
1407 \fn template <typename T> T QProperty<T>::value() const
1408
1409 Returns the value of the property. This may evaluate a binding expression that
1410 is tied to this property, before returning the value.
1411*/
1412
1413/*!
1414 \fn template <typename T> void QProperty<T>::setValue(rvalue_ref newValue)
1415 \fn template <typename T> void QProperty<T>::setValue(parameter_type newValue)
1416
1417 Assigns \a newValue to this property and removes the property's associated
1418 binding, if present.
1419*/
1420
1421/*!
1422 \fn template <typename T> QProperty<T> &QProperty<T>::operator=(rvalue_ref newValue)
1423 \fn template <typename T> QProperty<T> &QProperty<T>::operator=(parameter_type newValue)
1424
1425 Assigns \a newValue to this property and returns a reference to this QProperty.
1426*/
1427
1428/*!
1429 \fn template <typename T> QPropertyBinding<T> QProperty<T>::setBinding(const QPropertyBinding<T> &newBinding)
1430
1431 Associates the value of this property with the provided \a newBinding
1432 expression and returns the previously associated binding. The property's value
1433 is set to the result of evaluating the new binding. Whenever a dependency of
1434 the binding changes, the binding will be re-evaluated, and the property's
1435 value gets updated accordingly.
1436*/
1437
1438/*!
1439 \fn template <typename T> template <typename Functor> QPropertyBinding<T> QProperty<T>::setBinding(Functor f)
1440 \overload
1441 \since 6.0
1442
1443 Associates the value of this property with the provided functor \a f and
1444 returns the previously associated binding. The property's value is set to the
1445 result of evaluating the new binding. Whenever a dependency of the binding
1446 changes, the binding will be re-evaluated, and the property's value gets
1447 updated accordingly.
1448
1449 \sa {Formulating a Property Binding}
1450*/
1451
1452/*!
1453 \fn template <typename T> QPropertyBinding<T> bool QProperty<T>::setBinding(const QUntypedPropertyBinding &newBinding)
1454 \overload
1455
1456 Associates the value of this property with the provided \a newBinding
1457 expression. The property's value is set to the result of evaluating the new
1458 binding. Whenever a dependency of the binding changes, the binding will be
1459 re-evaluated, and the property's value gets updated accordingly.
1460
1461
1462 Returns true if the type of this property is the same as the type the binding
1463 function returns; false otherwise.
1464*/
1465
1466/*!
1467 \fn template <typename T> QPropertyBinding<T> QProperty<T>::binding() const
1468
1469 Returns the binding expression that is associated with this property. A
1470 default constructed QPropertyBinding<T> will be returned if no such
1471 association exists.
1472*/
1473
1474/*!
1475 \fn template <typename T> QPropertyBinding<T> QProperty<T>::takeBinding()
1476
1477 Disassociates the binding expression from this property and returns it. After
1478 calling this function, the value of the property will only change if you
1479 assign a new value to it, or when a new binding is set.
1480*/
1481
1482/*!
1483 \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QProperty<T>::onValueChanged(Functor f)
1484
1485 Registers the given functor \a f as a callback that shall be called whenever
1486 the value of the property changes. On each value change, the handler
1487 is either called immediately, or deferred, depending on the context.
1488
1489 The callback \a f is expected to be a type that has a plain call operator
1490 \c{()} without any parameters. This means that you can provide a C++ lambda
1491 expression, a std::function or even a custom struct with a call operator.
1492
1493 The returned property change handler object keeps track of the registration.
1494 When it goes out of scope, the callback is de-registered.
1495*/
1496
1497/*!
1498 \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QProperty<T>::subscribe(Functor f)
1499 \since 6.0
1500
1501 Subscribes the given functor \a f as a callback that is called immediately and
1502 whenever the value of the property changes in the future. On each value
1503 change, the handler is either called immediately, or deferred, depending on
1504 the context.
1505
1506 The callback \a f is expected to be a type that can be copied and has a plain
1507 call operator() without any parameters. This means that you can provide a C++
1508 lambda expression, a std::function or even a custom struct with a call
1509 operator.
1510
1511 The returned property change handler object keeps track of the subscription.
1512 When it goes out of scope, the callback is unsubscribed.
1513*/
1514
1515/*!
1516 \fn template <typename T> template <typename Functor> QPropertyNotifier QProperty<T>::addNotifier(Functor f)
1517 \since 6.2
1518
1519 Subscribes the given functor \a f as a callback that is called whenever
1520 the value of the property changes.
1521
1522 The callback \a f is expected to be a type that has a plain call operator
1523 \c{()} without any parameters. This means that you can provide a C++ lambda
1524 expression, a std::function or even a custom struct with a call operator.
1525
1526 The returned property change handler object keeps track of the subscription.
1527 When it goes out of scope, the callback is unsubscribed.
1528
1529 This method is in some cases easier to use than onValueChanged(), as the
1530 returned object is not a template. It can therefore more easily be stored,
1531 e.g. as a member in a class.
1532
1533 \sa onValueChanged(), subscribe()
1534*/
1535
1536/*!
1537 \fn template <typename T> QtPrivate::QPropertyBindingData &QProperty<T>::bindingData() const
1538 \internal
1539*/
1540
1541/*!
1542 \class QObjectBindableProperty
1543 \inmodule QtCore
1544 \brief The QObjectBindableProperty class is a template class that enables
1545 automatic property bindings for property data stored in QObject derived
1546 classes.
1547 \since 6.0
1548 \compares equality
1549 \compareswith equality T
1550 \endcompareswith
1551
1552 \ingroup tools
1553
1554 QObjectBindableProperty is a generic container that holds an
1555 instance of T and behaves mostly like \l QProperty.
1556 It is one of the classes implementing \l {Qt Bindable Properties}.
1557 Unlike QProperty, it stores its management data structure in
1558 the surrounding QObject.
1559 The extra template parameters are used to identify the surrounding
1560 class and a member function of that class acting as a change handler.
1561
1562 You can use QObjectBindableProperty to add binding support to code that uses
1563 Q_PROPERTY. The getter and setter methods must be adapted carefully according
1564 to the rules described in \l {Bindable Property Getters and Setters}.
1565
1566 In order to invoke the change signal on property changes, use
1567 QObjectBindableProperty and pass the change signal as a callback.
1568
1569 A simple example is given in the following.
1570
1571 \snippet code/src_corelib_kernel_qproperty.cpp 4
1572
1573 QObjectBindableProperty is usually not used directly, instead an instance of
1574 it is created by using the Q_OBJECT_BINDABLE_PROPERTY macro.
1575
1576 Use the Q_OBJECT_BINDABLE_PROPERTY macro in the class declaration to declare
1577 the property as bindable.
1578
1579 \snippet code/src_corelib_kernel_qproperty.cpp 0
1580
1581 If you need to directly initialize the property with some non-default value,
1582 you can use the Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS macro. It accepts a
1583 value for the initialization as one of its parameters.
1584
1585 \snippet code/src_corelib_kernel_qproperty.cpp 1
1586
1587 Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS does not support multiple arguments
1588 directly. If your property requires multiple arguments for initialization,
1589 please explicitly call the specific constructor.
1590
1591 \snippet code/src_corelib_kernel_qproperty.cpp 2
1592
1593 The change handler can optionally accept one argument, of the same type as the
1594 property, in which case it is passed the new value of the property. Otherwise,
1595 it should take no arguments.
1596
1597 If the property does not need a changed notification, you can leave out the
1598 "NOTIFY xChanged" in the Q_PROPERTY macro as well as the last argument
1599 of the Q_OBJECT_BINDABLE_PROPERTY and Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS
1600 macros.
1601
1602 \sa Q_OBJECT_BINDABLE_PROPERTY, Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS,
1603 QProperty, QObjectComputedProperty, {Qt's Property System}, {Qt Bindable
1604 Properties}
1605*/
1606
1607/*!
1608 \macro Q_OBJECT_BINDABLE_PROPERTY(containingClass, type, name, signal)
1609 \since 6.0
1610 \relates QObjectBindableProperty
1611 \brief Declares a \l QObjectBindableProperty inside \a containingClass of type
1612 \a type with name \a name. If the optional argument \a signal is given, this
1613 signal will be emitted when the property is marked dirty.
1614
1615 \sa {Qt's Property System}, {Qt Bindable Properties}
1616*/
1617
1618/*!
1619 \macro Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(containingClass, type, name, initialvalue, signal)
1620 \since 6.0
1621 \relates QObjectBindableProperty
1622 \brief Declares a \l QObjectBindableProperty inside \a containingClass
1623 of type \a type with name \a name which is initialized to \a initialvalue.
1624 If the optional argument \a signal is given, this signal will be emitted when
1625 the property is marked dirty.
1626
1627 \sa {Qt's Property System}, {Qt Bindable Properties}
1628*/
1629
1630/*!
1631 \class QObjectCompatProperty
1632 \inmodule QtCore
1633 \brief The QObjectCompatProperty class is a template class to help port old
1634 properties to the bindable property system.
1635 \since 6.0
1636 \ingroup tools
1637 \internal
1638
1639 QObjectCompatProperty is a generic container that holds an
1640 instance of \c T and behaves mostly like QProperty, just like
1641 QObjectBindableProperty. It's one of the Qt internal classes implementing
1642 \l {Qt Bindable Properties}. Like QObjectBindableProperty,
1643 QObjectCompatProperty stores its management data structure in the surrounding
1644 QObject. The last template parameter specifies a method (of the owning
1645 class) to be called when the property is changed through the binding.
1646 This is usually a setter.
1647
1648 As explained in \l {Qt Bindable Properties}, getters and setters for bindable
1649 properties have to be almost trivial to be correct. However, in legacy code,
1650 there is often complex logic in the setter. QObjectCompatProperty is a helper
1651 to port these properties to the bindable property system.
1652
1653 With QObjectCompatProperty, the same rules as described in
1654 \l {Bindable Property Getters and Setters} hold for the getter.
1655 For the setter, the rules are different. It remains that every possible code
1656 path in the setter must write to the underlying QObjectCompatProperty,
1657 otherwise calling the setter might not remove a pre-existing binding, as
1658 it should. However, as QObjectCompatProperty will call the setter on every
1659 change, the setter is allowed to contain code like updating class internals
1660 or emitting signals. Every write to the QObjectCompatProperty has to
1661 be analyzed carefully to comply with the rules given in
1662 \l {Writing to a Bindable Property}.
1663
1664 \section2 Properties with Virtual Setters
1665
1666 Some of the pre-existing Qt classes (for example, \l QAbstractProxyModel)
1667 have properties with virtual setters. Special care must be taken when
1668 making such properties bindable.
1669
1670 For the binding to work properly, the property must be correctly handled in
1671 all reimplemented methods of each derived class.
1672
1673 Unless the derived class has access to the underlying property object, the
1674 base implementation \e must be called for the binding to work correctly.
1675
1676 If the derived class can directly access the property instance, there is no
1677 need to explicitly call the base implementation, but the property's value
1678 \e must be correctly updated.
1679
1680 Refer to \l {Bindable Properties with Virtual Setters and Getters} for more
1681 details.
1682
1683 In both cases the expected behavior \e must be documented in the property's
1684 documentation, so that users can correctly override the setter.
1685
1686 Properties for which these conditions cannot be met should not be made
1687 bindable.
1688
1689 \sa Q_OBJECT_COMPAT_PROPERTY, QObjectBindableProperty, {Qt's Property System}, {Qt Bindable
1690 Properties}
1691*/
1692
1693/*!
1694 \macro Q_OBJECT_COMPAT_PROPERTY(containingClass, type, name, callback)
1695 \since 6.0
1696 \relates QObjectCompatProperty
1697 \internal
1698 \brief Declares a \l QObjectCompatProperty inside \a containingClass
1699 of type \a type with name \a name. The argument \a callback specifies
1700 a setter function to be called when the property is changed through the binding.
1701
1702 \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
1703*/
1704
1705/*!
1706 \macro Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(containingClass, type, name, callback, value)
1707 \since 6.0
1708 \relates QObjectCompatProperty
1709 \internal
1710 \brief Declares a \l QObjectCompatProperty inside of \a containingClass
1711 of type \a type with name \a name. The argument \a callback specifies
1712 a setter function to be called when the property is changed through the binding.
1713 \a value specifies an initialization value.
1714*/
1715
1716/*!
1717 \class QObjectComputedProperty
1718 \inmodule QtCore
1719 \brief The QObjectComputedProperty class is a template class to help port old
1720 properties to the bindable property system.
1721 \since 6.0
1722 \ingroup tools
1723
1724 QObjectComputedProperty is a read-only property which is recomputed on each read.
1725 It does not store the computed value.
1726 It is one of the Qt internal classes implementing \l {Qt Bindable Properties}.
1727 QObjectComputedProperty is usually not used directly, instead an instance of it is created by
1728 using the Q_OBJECT_COMPUTED_PROPERTY macro.
1729
1730 See the following example.
1731
1732 \snippet code/src_corelib_kernel_qproperty.cpp 5
1733
1734 The rules for getters in \l {Bindable Property Getters and Setters}
1735 also apply for QObjectComputedProperty. Especially, the getter
1736 should be trivial and only return the value of the QObjectComputedProperty object.
1737 The callback given to the QObjectComputedProperty should usually be a private
1738 method which is only called by the QObjectComputedProperty.
1739
1740 No setter is required or allowed, as QObjectComputedProperty is read-only.
1741
1742 To correctly participate in dependency handling, QObjectComputedProperty
1743 has to know when its value, the result of the callback given to it, might
1744 have changed. Whenever a bindable property used in the callback changes,
1745 this happens automatically. If the result of the callback might change
1746 because of a change in a value which is not a bindable property,
1747 it is the developer's responsibility to call \c notify
1748 on the QObjectComputedProperty object.
1749 This will inform dependent properties about the potential change.
1750
1751 Note that calling \c notify might trigger change handlers in dependent
1752 properties, which might in turn use the object the QObjectComputedProperty
1753 is a member of. So \c notify must not be called when in a transitional
1754 or invalid state.
1755
1756 QObjectComputedProperty is not suitable for use with a computation that depends
1757 on any input that might change without notice, such as the contents of a file.
1758
1759 \sa Q_OBJECT_COMPUTED_PROPERTY, QProperty, QObjectBindableProperty,
1760 {Qt's Property System}, {Qt Bindable Properties}
1761*/
1762
1763/*!
1764 \macro Q_OBJECT_COMPUTED_PROPERTY(containingClass, type, name, callback)
1765 \since 6.0
1766 \relates QObjectComputedProperty
1767 \brief Declares a \l QObjectComputedProperty inside \a containingClass
1768 of type \a type with name \a name. The argument \a callback specifies
1769 a GETTER function to be called when the property is evaluated.
1770
1771 \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
1772*/
1773
1774/*!
1775 \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty()
1776
1777 Constructs a property with a default constructed instance of T.
1778*/
1779
1780/*!
1781 \fn template <typename Class, typename T, auto offset, auto Callback> explicit QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(const T &initialValue)
1782
1783 Constructs a property with the provided \a initialValue.
1784*/
1785
1786/*!
1787 \fn template <typename Class, typename T, auto offset, auto Callback> explicit QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(T &&initialValue)
1788
1789 Move-Constructs a property with the provided \a initialValue.
1790*/
1791
1792/*!
1793 \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, const QPropertyBinding<T> &binding)
1794
1795 Constructs a property that is tied to the provided \a binding expression.
1796 The property's value is set to the result of evaluating the new binding.
1797 Whenever a dependency of the binding changes, the binding will be
1798 re-evaluated, and the property's value gets updated accordingly.
1799
1800 When the property value changes, \a owner is notified via the Callback
1801 function.
1802*/
1803
1804/*!
1805 \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, QPropertyBinding<T> &&binding)
1806
1807 Constructs a property that is tied to the provided \a binding expression.
1808 The property's value is set to the result of evaluating the new binding.
1809 Whenever a dependency of the binding changes, the binding will be
1810 re-evaluated, and the property's value gets updated accordingly.
1811
1812 When the property value changes, \a
1813 owner is notified via the Callback function.
1814*/
1815
1816/*!
1817 \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Functor &&f)
1818
1819 Constructs a property that is tied to the provided binding expression \a f.
1820 The property's value is set to the result of evaluating the new binding.
1821 Whenever a dependency of the binding changes, the binding will be
1822 re-evaluated, and the property's value gets updated accordingly.
1823
1824*/
1825
1826/*!
1827 \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::~QObjectBindableProperty()
1828
1829 Destroys the property.
1830*/
1831
1832/*!
1833 \fn template <typename Class, typename T, auto offset, auto Callback> T QObjectBindableProperty<Class, T, offset, Callback>::value() const
1834
1835 Returns the value of the property. This may evaluate a binding expression that
1836 is tied to this property, before returning the value.
1837*/
1838
1839/*!
1840 \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::setValue(parameter_type newValue)
1841 \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::setValue(rvalue_ref newValue)
1842
1843 Assigns \a newValue to this property and removes the property's associated
1844 binding, if present. If the property value changes as a result, calls the
1845 Callback function on \a owner.
1846*/
1847
1848/*!
1849 \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::notify()
1850
1851 Programmatically signals a change of the property. Any binding which depend on
1852 it will be notified, and if the property has a signal, it will be emitted.
1853
1854 This can be useful in combination with setValueBypassingBindings to defer
1855 signalling the change until a class invariant has been restored.
1856
1857 \note If this property has a binding (i.e. hasBinding() returns true), that
1858 binding is not reevaluated when notify() is called. Any binding depending on
1859 this property is still reevaluated as usual.
1860
1861 \sa Qt::beginPropertyUpdateGroup(), setValueBypassingBindings()
1862*/
1863
1864/*!
1865 \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QPropertyBinding<T> &newBinding)
1866
1867 Associates the value of this property with the provided \a newBinding
1868 expression and returns the previously associated binding.
1869 The property's value is set to the result of evaluating the new binding. Whenever a dependency of
1870 the binding changes, the binding will be re-evaluated,
1871 and the property's value gets updated accordingly.
1872 When the property value changes, the owner
1873 is notified via the Callback function.
1874*/
1875
1876/*!
1877 \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(Functor f)
1878 \overload
1879
1880 Associates the value of this property with the provided functor \a f and
1881 returns the previously associated binding. The property's value is set to the
1882 result of evaluating the new binding by invoking the call operator \c{()} of \a
1883 f. Whenever a dependency of the binding changes, the binding will be
1884 re-evaluated, and the property's value gets updated accordingly.
1885
1886 When the property value changes, the owner is notified via the Callback
1887 function.
1888
1889 \sa {Formulating a Property Binding}
1890*/
1891
1892/*!
1893 \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> bool QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QUntypedPropertyBinding &newBinding)
1894 \overload
1895
1896 Associates the value of this property with the provided \a newBinding
1897 expression. The property's value is set to the result of evaluating the new
1898 binding. Whenever a dependency of the binding changes, the binding will be
1899 re-evaluated, and the property's value gets updated accordingly.
1900
1901
1902 Returns \c true if the type of this property is the same as the type the
1903 binding function returns; \c false otherwise.
1904*/
1905
1906/*!
1907 \fn template <typename Class, typename T, auto offset, auto Callback> bool QObjectBindableProperty<Class, T, offset, Callback>::hasBinding() const
1908
1909 Returns true if the property is associated with a binding; false otherwise.
1910*/
1911
1912
1913/*!
1914 \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::binding() const
1915
1916 Returns the binding expression that is associated with this property. A
1917 default constructed QPropertyBinding<T> will be returned if no such
1918 association exists.
1919*/
1920
1921/*!
1922 \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::takeBinding()
1923
1924 Disassociates the binding expression from this property and returns it. After
1925 calling this function, the value of the property will only change if you
1926 assign a new value to it, or when a new binding is set.
1927*/
1928
1929/*!
1930 \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::onValueChanged(Functor f)
1931
1932 Registers the given functor \a f as a callback that shall be called whenever
1933 the value of the property changes. On each value change, the handler is either
1934 called immediately, or deferred, depending on the context.
1935
1936 The callback \a f is expected to be a type that has a plain call operator
1937 \c{()} without any parameters. This means that you can provide a C++ lambda
1938 expression, a std::function or even a custom struct with a call operator.
1939
1940 The returned property change handler object keeps track of the registration.
1941 When it goes out of scope, the callback is de-registered.
1942*/
1943
1944/*!
1945 \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::subscribe(Functor f)
1946
1947 Subscribes the given functor \a f as a callback that is called immediately and
1948 whenever the value of the property changes in the future. On each value
1949 change, the handler is either called immediately, or deferred, depending on
1950 the context.
1951
1952 The callback \a f is expected to be a type that has a plain call operator
1953 \c{()} without any parameters. This means that you can provide a C++ lambda
1954 expression, a std::function or even a custom struct with a call operator.
1955
1956 The returned property change handler object keeps track of the subscription.
1957 When it goes out of scope, the callback is unsubscribed.
1958*/
1959
1960/*!
1961 \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyNotifier QObjectBindableProperty<Class, T, offset, Callback>::addNotifier(Functor f)
1962
1963 Subscribes the given functor \a f as a callback that is called whenever the
1964 value of the property changes.
1965
1966 The callback \a f is expected to be a type that has a plain call operator
1967 \c{()} without any parameters. This means that you can provide a C++ lambda
1968 expression, a std::function or even a custom struct with a call operator.
1969
1970 The returned property change handler object keeps track of the subscription.
1971 When it goes out of scope, the callback is unsubscribed.
1972
1973 This method is in some cases easier to use than onValueChanged(), as the
1974 returned object is not a template. It can therefore more easily be stored,
1975 e.g. as a member in a class.
1976
1977 \sa onValueChanged(), subscribe()
1978*/
1979
1980/*!
1981 \fn template <typename T> QtPrivate::QPropertyBase &QObjectBindableProperty<Class, T, offset, Callback>::propertyBase() const
1982 \internal
1983*/
1984
1985/*!
1986 \class QPropertyChangeHandler
1987 \inmodule QtCore
1988 \brief The QPropertyChangeHandler class controls the lifecycle of change
1989 callback installed on a QProperty.
1990
1991 \ingroup tools
1992
1993 QPropertyChangeHandler\<Functor\> is created when registering a callback on a
1994 QProperty to listen to changes to the property's value, using
1995 QProperty::onValueChanged and QProperty::subscribe. As long as the change
1996 handler is alive, the callback remains installed.
1997
1998 A handler instance can be transferred between C++ scopes using move semantics.
1999*/
2000
2001/*!
2002 \class QPropertyNotifier
2003 \inmodule QtCore
2004 \brief The QPropertyNotifier class controls the lifecycle of change callback installed on a QProperty.
2005
2006 \ingroup tools
2007 \since 6.2
2008
2009 QPropertyNotifier is created when registering a callback on a QProperty to
2010 listen to changes to the property's value, using QProperty::addNotifier. As
2011 long as the change handler is alive, the callback remains installed.
2012
2013 A handler instance can be transferred between C++ scopes using move semantics.
2014*/
2015
2016/*!
2017 \class QPropertyAlias
2018 \inmodule QtCore
2019 \internal
2020
2021 \brief The QPropertyAlias class is a safe alias for a QProperty with same
2022 template parameter.
2023
2024 \ingroup tools
2025
2026 QPropertyAlias\<T\> wraps a pointer to a QProperty\<T\> and automatically
2027 invalidates itself when the QProperty\<T\> is destroyed. It forwards all
2028 method invocations to the wrapped property. For example:
2029
2030 \code
2031 QProperty<QString> *name = new QProperty<QString>("John");
2032 QProperty<int> age(41);
2033
2034 QPropertyAlias<QString> nameAlias(name);
2035 QPropertyAlias<int> ageAlias(&age);
2036
2037 QProperty<QString> fullname;
2038 fullname.setBinding([&]() { return nameAlias.value() + " age: " + QString::number(ageAlias.value()); });
2039
2040 qDebug() << fullname.value(); // Prints "John age: 41"
2041
2042 *name = "Emma"; // Marks binding expression as dirty
2043
2044 qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 41"
2045
2046 // Birthday is coming up
2047 ageAlias.setValue(age.value() + 1); // Writes the age property through the alias
2048
2049 qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 42"
2050
2051 delete name; // Leaves the alias in an invalid, but accessible state
2052 nameAlias.setValue("Eve"); // Ignored: nameAlias carries a default-constructed QString now
2053
2054 ageAlias.setValue(92);
2055 qDebug() << fullname.value(); // Re-evaluates the binding expression and prints " age: 92"
2056 \endcode
2057*/
2058
2059/*!
2060 \fn template <typename T> QPropertyAlias<T>::QPropertyAlias(QProperty<T> *property)
2061
2062 Constructs a property alias for the given \a property.
2063*/
2064
2065/*!
2066 \fn template <typename T> explicit QPropertyAlias<T>::QPropertyAlias(QPropertyAlias<T> *alias)
2067
2068 Constructs a property alias for the property aliased by \a alias.
2069*/
2070
2071/*!
2072 \fn template <typename T> T QPropertyAlias<T>::value() const
2073
2074 Returns the value of the aliased property. This may evaluate a binding
2075 expression that is tied to the property, before returning the value.
2076*/
2077
2078/*!
2079 \fn template <typename T> QPropertyAlias<T>::operator T() const
2080
2081 Returns the value of the aliased property. This may evaluate a binding
2082 expression that is tied to the property, before returning the value.
2083*/
2084
2085/*!
2086 \fn template <typename T> void QPropertyAlias<T>::setValue(const T &newValue)
2087
2088 Assigns \a newValue to the aliased property and removes the property's
2089 associated binding, if present.
2090*/
2091
2092/*!
2093 \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const T &newValue)
2094
2095 Assigns \a newValue to the aliased property and returns a reference to this
2096 QPropertyAlias.
2097*/
2098
2099/*!
2100 \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::setBinding(const QPropertyBinding<T> &newBinding)
2101
2102 Associates the value of the aliased property with the provided \a newBinding
2103 expression and returns any previous binding the associated with the aliased
2104 property.The property's value is set to the result of evaluating the new
2105 binding. Whenever a dependency of the binding changes, the binding will be
2106 re-evaluated, and the property's value gets updated accordingly.
2107
2108
2109 Returns any previous binding associated with the property, or a
2110 default-constructed QPropertyBinding<T>.
2111*/
2112
2113/*!
2114 \fn template <typename T> QPropertyBinding<T> bool QPropertyAlias<T>::setBinding(const QUntypedPropertyBinding &newBinding)
2115 \overload
2116
2117 Associates the value of the aliased property with the provided \a newBinding
2118 expression. The property's value is set to the result of evaluating the new
2119 binding. Whenever a dependency of the binding changes, the binding will be
2120 re-evaluated, and the property's value gets updated accordingly.
2121
2122
2123 Returns true if the type of this property is the same as the type the binding
2124 function returns; false otherwise.
2125*/
2126
2127/*!
2128 \fn template <typename T> template <typename Functor> QPropertyBinding<T> QPropertyAlias<T>::setBinding(Functor f)
2129 \overload
2130
2131 Associates the value of the aliased property with the provided functor \a f
2132 expression. The property's value is set to the result of evaluating the new
2133 binding. Whenever a dependency of the binding changes, the binding will be
2134 re-evaluated, and the property's value gets updated accordingly.
2135
2136
2137 Returns any previous binding associated with the property, or a
2138 default-constructed QPropertyBinding<T>.
2139
2140 \sa {Formulating a Property Binding}
2141*/
2142
2143/*!
2144 \fn template <typename T> bool QPropertyAlias<T>::hasBinding() const
2145
2146 Returns true if the aliased property is associated with a binding; false
2147 otherwise.
2148*/
2149
2150/*!
2151 \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::binding() const
2152
2153 Returns the binding expression that is associated with the aliased property. A
2154 default constructed QPropertyBinding<T> will be returned if no such
2155 association exists.
2156*/
2157
2158/*!
2159 \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::takeBinding()
2160
2161 Disassociates the binding expression from the aliased property and returns it.
2162 After calling this function, the value of the property will only change if
2163 you assign a new value to it, or when a new binding is set.
2164*/
2165
2166/*!
2167 \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::onValueChanged(Functor f)
2168
2169 Registers the given functor \a f as a callback that shall be called whenever
2170 the value of the aliased property changes. On each value change, the handler
2171 is either called immediately, or deferred, depending on the context.
2172
2173 The callback \a f is expected to be a type that has a plain call operator
2174 \c{()} without any parameters. This means that you can provide a C++ lambda
2175 expression, a std::function or even a custom struct with a call operator.
2176
2177 The returned property change handler object keeps track of the registration. When it
2178 goes out of scope, the callback is de-registered.
2179*/
2180
2181/*!
2182 \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::subscribe(Functor f)
2183
2184 Subscribes the given functor \a f as a callback that is called immediately and
2185 whenever the value of the aliased property changes in the future. On each
2186 value change, the handler is either called immediately, or deferred, depending
2187 on the context.
2188
2189 The callback \a f is expected to be a type that has a plain call operator
2190 \c{()} without any parameters. This means that you can provide a C++ lambda
2191 expression, a std::function or even a custom struct with a call operator.
2192
2193 The returned property change handler object keeps track of the subscription.
2194 When it goes out of scope, the callback is unsubscribed.
2195*/
2196
2197/*!
2198 \fn template <typename T> template <typename Functor> QPropertyNotifier QPropertyAlias<T>::addNotifier(Functor f)
2199
2200 Subscribes the given functor \a f as a callback that is called whenever
2201 the value of the aliased property changes.
2202
2203 The callback \a f is expected to be a type that has a plain call operator
2204 \c{()} without any parameters. This means that you can provide a C++ lambda
2205 expression, a std::function or even a custom struct with a call operator.
2206
2207 The returned property change handler object keeps track of the subscription.
2208 When it goes out of scope, the callback is unsubscribed.
2209
2210 This method is in some cases easier to use than onValueChanged(), as the
2211 returned object is not a template. It can therefore more easily be stored,
2212 e.g. as a member in a class.
2213
2214 \sa onValueChanged(), subscribe()
2215*/
2216
2217/*!
2218 \fn template <typename T> bool QPropertyAlias<T>::isValid() const
2219
2220 Returns true if the aliased property still exists; false otherwise.
2221
2222 If the aliased property doesn't exist, all other method calls are ignored.
2223*/
2224
2225struct QBindingStorageData
2226{
2227 size_t size = 0;
2228 size_t used = 0;
2229 // Pair[] pairs;
2230};
2231
2232struct QBindingStoragePrivate
2233{
2234 // This class basically implements a simple and fast hash map to store bindings for a QObject
2235 // The reason that we're not using QHash is that QPropertyBindingData can not be copied, only
2236 // moved. That doesn't work well together with an implicitly shared class.
2237 struct Pair
2238 {
2239 QUntypedPropertyData *data;
2240 QPropertyBindingData bindingData;
2241 };
2242 static_assert(alignof(Pair) == alignof(void *));
2243 static_assert(alignof(size_t) == alignof(void *));
2244
2245 QBindingStorageData *&d;
2246
2247 static inline Pair *pairs(QBindingStorageData *dd)
2248 {
2249 Q_ASSERT(dd);
2250 return reinterpret_cast<Pair *>(dd + 1);
2251 }
2252 void reallocate(size_t newSize)
2253 {
2254 Q_ASSERT(!d || newSize > d->size);
2255 size_t allocSize = sizeof(QBindingStorageData) + newSize*sizeof(Pair);
2256 void *nd = calloc(nmemb: 1, size: allocSize);
2257 QBindingStorageData *newData = new (nd) QBindingStorageData;
2258 newData->size = newSize;
2259 if (!d) {
2260 d = newData;
2261 return;
2262 }
2263 newData->used = d->used;
2264 Pair *p = pairs(dd: d);
2265 for (size_t i = 0; i < d->size; ++i, ++p) {
2266 if (p->data) {
2267 Pair *pp = pairs(dd: newData);
2268 Q_ASSERT(newData->size && (newData->size & (newData->size - 1)) == 0); // size is a power of two
2269 size_t index = qHash(key: p->data) & (newData->size - 1);
2270 while (pp[index].data) {
2271 ++index;
2272 if (index == newData->size)
2273 index = 0;
2274 }
2275 new (pp + index) Pair{.data: p->data, .bindingData: QPropertyBindingData(std::move(p->bindingData))};
2276 }
2277 }
2278 // data has been moved, no need to call destructors on old Pairs
2279 const size_t oldAllocSize = sizeof(QBindingStorageData) + d->size*sizeof(Pair);
2280 QtPrivate::sizedFree(ptr: d, allocSize: oldAllocSize);
2281 d = newData;
2282 }
2283
2284 QBindingStoragePrivate(QBindingStorageData *&_d) : d(_d) {}
2285
2286 QPropertyBindingData *get(const QUntypedPropertyData *data)
2287 {
2288 Q_ASSERT(d);
2289 Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two
2290 size_t index = qHash(key: data) & (d->size - 1);
2291 Pair *p = pairs(dd: d);
2292 while (p[index].data) {
2293 if (p[index].data == data)
2294 return &p[index].bindingData;
2295 ++index;
2296 if (index == d->size)
2297 index = 0;
2298 }
2299 return nullptr;
2300 }
2301 QPropertyBindingData *get(QUntypedPropertyData *data, bool create)
2302 {
2303 if (!d) {
2304 if (!create)
2305 return nullptr;
2306 reallocate(newSize: 8);
2307 }
2308 else if (d->used*2 >= d->size)
2309 reallocate(newSize: d->size*2);
2310 Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two
2311 size_t index = qHash(key: data) & (d->size - 1);
2312 Pair *p = pairs(dd: d);
2313 while (p[index].data) {
2314 if (p[index].data == data)
2315 return &p[index].bindingData;
2316 ++index;
2317 if (index == d->size)
2318 index = 0;
2319 }
2320 if (!create)
2321 return nullptr;
2322 ++d->used;
2323 new (p + index) Pair{.data: data, .bindingData: QPropertyBindingData()};
2324 return &p[index].bindingData;
2325 }
2326
2327 void destroy()
2328 {
2329 if (!d)
2330 return;
2331 Pair *p = pairs(dd: d);
2332 for (size_t i = 0; i < d->size; ++i) {
2333 if (p->data)
2334 p->~Pair();
2335 ++p;
2336 }
2337 const size_t allocSize = sizeof(QBindingStorageData) + d->size*sizeof(Pair);
2338 QtPrivate::sizedFree(ptr: d, allocSize);
2339 }
2340};
2341
2342/*!
2343 \class QBindingStorage
2344 \internal
2345
2346 QBindingStorage acts as a storage for property binding related data in QObject.
2347 Any property in a QObject can be made bindable by using the Q_OBJECT_BINDABLE_PROPERTY
2348 macro to declare it. A setter and a getter for the property and a declaration using
2349 Q_PROPERTY have to be made as usual.
2350 Binding related data will automatically be stored within the QBindingStorage
2351 inside the QObject.
2352*/
2353
2354QBindingStorage::QBindingStorage()
2355{
2356 bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus)();
2357 Q_ASSERT(bindingStatus);
2358}
2359
2360QBindingStorage::~QBindingStorage()
2361{
2362 QBindingStoragePrivate(d).destroy();
2363}
2364
2365void QBindingStorage::reinitAfterThreadMove()
2366{
2367 bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus)();
2368 Q_ASSERT(bindingStatus);
2369}
2370
2371void QBindingStorage::clear()
2372{
2373 QBindingStoragePrivate(d).destroy();
2374 d = nullptr;
2375 bindingStatus = nullptr;
2376}
2377
2378void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
2379{
2380 Q_ASSERT(bindingStatus);
2381 // Use ::bindingStatus to get the binding from TLS. This is required, so that reads from
2382 // another thread do not register as dependencies
2383 QtPrivate::BindingEvaluationState *currentBinding;
2384#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
2385 const bool threadMatches = (QThread::currentThreadId() == bindingStatus->threadId);
2386 if (Q_LIKELY(threadMatches))
2387 currentBinding = bindingStatus->currentlyEvaluatingBinding;
2388 else
2389 currentBinding = QT_PREPEND_NAMESPACE(bindingStatus)().currentlyEvaluatingBinding;
2390#else
2391 currentBinding = QT_PREPEND_NAMESPACE(bindingStatus)().currentlyEvaluatingBinding;
2392#endif
2393 QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
2394 if (!currentBinding)
2395 return;
2396 auto storage = QBindingStoragePrivate(d).get(data: dd, create: true);
2397 if (!storage)
2398 return;
2399 storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
2400}
2401
2402
2403QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
2404{
2405 return QBindingStoragePrivate(d).get(data);
2406}
2407
2408const QBindingStatus *QBindingStorage::status(QtPrivate::QBindingStatusAccessToken) const
2409{
2410 return bindingStatus;
2411}
2412
2413QPropertyBindingData *QBindingStorage::bindingData_helper(QUntypedPropertyData *data, bool create)
2414{
2415 return QBindingStoragePrivate(d).get(data, create);
2416}
2417
2418
2419namespace QtPrivate {
2420
2421
2422void initBindingStatusThreadId()
2423{
2424 bindingStatus().threadId = QThread::currentThreadId();
2425}
2426
2427BindingEvaluationState *suspendCurrentBindingStatus()
2428{
2429 auto ret = bindingStatus().currentlyEvaluatingBinding;
2430 bindingStatus().currentlyEvaluatingBinding = nullptr;
2431 return ret;
2432}
2433
2434void restoreBindingStatus(BindingEvaluationState *status)
2435{
2436 bindingStatus().currentlyEvaluatingBinding = status;
2437}
2438
2439/*!
2440 \internal
2441 This function can be used to detect whether we are currently
2442 evaluating a binding. This can e.g. be used to defer the allocation
2443 of extra data for a QPropertyBindingStorage in a getter.
2444 Note that this function accesses TLS storage, and is therefore soemwhat
2445 costly to call.
2446*/
2447bool isAnyBindingEvaluating()
2448{
2449 return bindingStatus().currentlyEvaluatingBinding != nullptr;
2450}
2451
2452bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
2453{
2454 // Accessing bindingStatus is expensive because it's thread-local. Do it only once.
2455 if (const auto current = bindingStatus().currentCompatProperty)
2456 return current->property == property;
2457 return false;
2458}
2459
2460namespace BindableWarnings {
2461
2462void printUnsuitableBindableWarning(QAnyStringView prefix, BindableWarnings::Reason reason)
2463{
2464 switch (reason) {
2465 case QtPrivate::BindableWarnings::NonBindableInterface:
2466 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2467 << "The QBindable does not allow interaction with the binding.";
2468 break;
2469 case QtPrivate::BindableWarnings::ReadOnlyInterface:
2470 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2471 << "The QBindable is read-only.";
2472 break;
2473 default:
2474 case QtPrivate::BindableWarnings::InvalidInterface:
2475 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2476 << "The QBindable is invalid.";
2477 break;
2478 }
2479}
2480
2481void printMetaTypeMismatch(QMetaType actual, QMetaType expected)
2482{
2483 qCWarning(lcQPropertyBinding) << "setBinding: Could not set binding as the property expects it to be of type"
2484 << actual.name()
2485 << "but got" << expected.name() << "instead.";
2486}
2487
2488} // namespace BindableWarnings end
2489
2490/*!
2491 \internal
2492 Returns the binding statusof the current thread.
2493 */
2494QBindingStatus* getBindingStatus(QtPrivate::QBindingStatusAccessToken)
2495{
2496 return &QT_PREPEND_NAMESPACE(bindingStatus)();
2497}
2498void setBindingStatus(QBindingStatus *status, QBindingStatusAccessToken)
2499{
2500 Q_ASSERT(!tl_status);
2501 Q_ASSERT(status);
2502 tl_status = status;
2503}
2504
2505namespace PropertyAdaptorSlotObjectHelpers {
2506void getter(const QUntypedPropertyData *d, void *value)
2507{
2508 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2509 adaptor->bindingData().registerWithCurrentlyEvaluatingBinding();
2510 auto mt = adaptor->metaProperty().metaType();
2511 mt.destruct(data: value);
2512 mt.construct(where: value, copy: adaptor->metaProperty().read(obj: adaptor->object()).data());
2513}
2514
2515void setter(QUntypedPropertyData *d, const void *value)
2516{
2517 auto adaptor = static_cast<QtPrivate::QPropertyAdaptorSlotObject *>(d);
2518 adaptor->bindingData().removeBinding();
2519 adaptor->metaProperty().write(obj: adaptor->object(),
2520 value: QVariant(adaptor->metaProperty().metaType(), value));
2521}
2522
2523QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d)
2524{
2525 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2526 return QUntypedPropertyBinding(adaptor->bindingData().binding());
2527}
2528
2529bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
2530 QtPrivate::QPropertyBindingFunction binding, QUntypedPropertyData *temp,
2531 void *value)
2532{
2533 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2534 type.destruct(data: value);
2535 type.construct(where: value, copy: adaptor->metaProperty().read(obj: adaptor->object()).data());
2536 if (binding.vtable->call(type, temp, binding.functor)) {
2537 adaptor->metaProperty().write(obj: adaptor->object(), value: QVariant(type, value));
2538 return true;
2539 }
2540 return false;
2541}
2542
2543QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding,
2544 QPropertyBindingWrapper wrapper)
2545{
2546 auto adaptor = static_cast<QPropertyAdaptorSlotObject *>(d);
2547 return adaptor->bindingData().setBinding(binding, propertyDataPtr: d, staticObserverCallback: nullptr, guardCallback: wrapper);
2548}
2549
2550void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer)
2551{
2552 observer->setSource(static_cast<const QPropertyAdaptorSlotObject *>(d)->bindingData());
2553}
2554}
2555
2556QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty &p)
2557 : QSlotObjectBase(&impl), obj(o), metaProperty_(p)
2558{
2559}
2560
2561#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
2562void QPropertyAdaptorSlotObject::impl(int which, QSlotObjectBase *this_, QObject *r, void **a,
2563 bool *ret)
2564#else
2565void QPropertyAdaptorSlotObject::impl(QSlotObjectBase *this_, QObject *r, void **a, int which,
2566 bool *ret)
2567#endif
2568{
2569 auto self = static_cast<QPropertyAdaptorSlotObject *>(this_);
2570 switch (which) {
2571 case Destroy:
2572 delete self;
2573 break;
2574 case Call:
2575 if (!self->bindingData_.hasBinding())
2576 self->bindingData_.notifyObservers(propertyDataPtr: self);
2577 break;
2578 case Compare:
2579 case NumOperations:
2580 Q_UNUSED(r);
2581 Q_UNUSED(a);
2582 Q_UNUSED(ret);
2583 break;
2584 }
2585}
2586
2587} // namespace QtPrivate end
2588
2589QUntypedBindable::QUntypedBindable(QObject *obj, const QMetaProperty &metaProperty,
2590 const QtPrivate::QBindableInterface *i)
2591 : iface(i)
2592{
2593 if (!obj)
2594 return;
2595
2596 if (!metaProperty.isValid()) {
2597 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property is not valid";
2598 return;
2599 }
2600
2601 if (metaProperty.isBindable()) {
2602 *this = metaProperty.bindable(object: obj);
2603 return;
2604 }
2605
2606 if (!metaProperty.hasNotifySignal()) {
2607 qCWarning(lcQPropertyBinding)
2608 << "QUntypedBindable: Property" << metaProperty.name() << "has no notify signal";
2609 return;
2610 }
2611
2612 auto metatype = iface->metaType();
2613 if (metaProperty.metaType() != metatype) {
2614 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
2615 << "of type" << metaProperty.metaType().name()
2616 << "does not match requested type" << metatype.name();
2617 return;
2618 }
2619
2620 // Test for name pointer equality proves it's exactly the same property
2621 if (obj->metaObject()->property(index: metaProperty.propertyIndex()).name() != metaProperty.name()) {
2622 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
2623 << "does not belong to this object";
2624 return;
2625 }
2626
2627 // Get existing binding data if it exists
2628 auto adaptor = QObjectPrivate::get(o: obj)->getPropertyAdaptorSlotObject(property: metaProperty);
2629
2630 if (!adaptor) {
2631 adaptor = new QPropertyAdaptorSlotObject(obj, metaProperty);
2632
2633 auto c = QObjectPrivate::connect(sender: obj, signal_index: metaProperty.notifySignalIndex(), receiver: obj, slotObj: adaptor,
2634 type: Qt::DirectConnection);
2635 Q_ASSERT(c);
2636 }
2637
2638 data = adaptor;
2639}
2640
2641QUntypedBindable::QUntypedBindable(QObject *obj, const char *property,
2642 const QtPrivate::QBindableInterface *i)
2643 : QUntypedBindable(
2644 obj,
2645 [=]() -> QMetaProperty {
2646 if (!obj)
2647 return {};
2648 auto propertyIndex = obj->metaObject()->indexOfProperty(name: property);
2649 if (propertyIndex < 0) {
2650 qCWarning(lcQPropertyBinding)
2651 << "QUntypedBindable: No property named" << property;
2652 return {};
2653 }
2654 return obj->metaObject()->property(index: propertyIndex);
2655 }(),
2656 i)
2657{
2658}
2659
2660QT_END_NAMESPACE
2661

source code of qtbase/src/corelib/kernel/qproperty.cpp