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