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