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