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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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