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

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