Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
8#include <QScopeGuard>
9#include <QtCore/qloggingcategory.h>
10#include <QThread>
11#include <QtCore/qmetaobject.h>
12
13#include "qobject_p.h"
14
16
17Q_LOGGING_CATEGORY(lcQPropertyBinding, "qt.qproperty.binding");
18
19using namespace QtPrivate;
20
25
27{
28 if (ptr != d) {
29 if (ptr)
30 ptr->addRef();
31 auto *old = std::exchange(d, ptr);
32 if (old && !old->deref())
34 }
35}
36
37
39{
40 if (auto *b = binding()) {
41 observer->prev = &b->firstObserver.ptr;
42 observer->next = b->firstObserver.ptr;
43 if (observer->next)
44 observer->next->prev = &observer->next;
45 b->firstObserver.ptr = observer;
46 } else {
47 auto &d = ptr->d_ref();
49 auto firstObserver = reinterpret_cast<QPropertyObserver*>(d);
50 observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
51 observer->next = firstObserver;
52 if (observer->next)
53 observer->next->prev = &observer->next;
54 d = reinterpret_cast<quintptr>(observer);
55 }
56}
57
68{
69 // we can't access the dynamic page size as we need a constant value
70 // use 4096 as a sensible default
71 static constexpr inline auto PageSize = 4096;
72 int ref = 0;
73 QPropertyDelayedNotifications *next = nullptr; // in case we have more than size dirty properties...
75 // Size chosen to avoid allocating more than one page of memory, while still ensuring
76 // that we can store many delayed properties without doing further allocations
77 static constexpr qsizetype size = (PageSize - 3*sizeof(void *))/sizeof(QPropertyProxyBindingData);
79
89 void addProperty(const QPropertyBindingData *bindingData, QUntypedPropertyData *propertyData) {
90 if (bindingData->isNotificationDelayed())
91 return;
92 auto *data = this;
93 while (data->used == size) {
94 if (!data->next)
95 // add a new page
97 data = data->next;
98 }
99 auto *delayed = data->delayedProperties + data->used;
100 *delayed = QPropertyProxyBindingData { bindingData->d_ptr, bindingData, propertyData };
101 ++data->used;
102 // preserve the binding bit for faster access
103 quintptr bindingBit = bindingData->d_ptr & QPropertyBindingData::BindingBit;
104 bindingData->d_ptr = reinterpret_cast<quintptr>(delayed) | QPropertyBindingData::DelayedNotificationBit | bindingBit;
105 Q_ASSERT(bindingData->d_ptr > 3);
106 if (!bindingBit) {
107 if (auto observer = reinterpret_cast<QPropertyObserver *>(delayed->d_ptr))
108 observer->prev = reinterpret_cast<QPropertyObserver **>(&delayed->d_ptr);
109 }
110 }
111
125 auto *delayed = delayedProperties + index;
126 auto *bindingData = delayed->originalBindingData;
127 if (!bindingData)
128 return;
129
130 bindingData->d_ptr = delayed->d_ptr;
132 if (!bindingData->hasBinding()) {
133 if (auto observer = reinterpret_cast<QPropertyObserver *>(bindingData->d_ptr))
134 observer->prev = reinterpret_cast<QPropertyObserver **>(&bindingData->d_ptr);
135 }
136
137 QPropertyBindingDataPointer bindingDataPointer{bindingData};
138 QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
139 if (observer)
140 observer.evaluateBindings(bindingObservers, status);
141 }
142
153 auto *delayed = delayedProperties + index;
154 if (delayed->d_ptr & QPropertyBindingData::BindingBit)
155 return; // already handled
156 if (!delayed->originalBindingData)
157 return;
158 delayed->originalBindingData = nullptr;
159
160 QPropertyObserverPointer observer { reinterpret_cast<QPropertyObserver *>(delayed->d_ptr & ~QPropertyBindingData::DelayedNotificationBit) };
161 delayed->d_ptr = 0;
162
163 if (observer)
164 observer.notify(delayed->propertyData);
165 }
166};
167
168Q_CONSTINIT static thread_local QBindingStatus bindingStatus;
169
191{
192 QPropertyDelayedNotifications *& groupUpdateData = bindingStatus.groupUpdateData;
193 if (!groupUpdateData)
194 groupUpdateData = new QPropertyDelayedNotifications;
195 ++groupUpdateData->ref;
196}
197
211{
212 auto status = &bindingStatus;
213 QPropertyDelayedNotifications *& groupUpdateData = status->groupUpdateData;
214 auto *data = groupUpdateData;
215 Q_ASSERT(data->ref);
216 if (--data->ref)
217 return;
218 groupUpdateData = nullptr;
219 // ensures that bindings are kept alive until endPropertyUpdateGroup concludes
220 PendingBindingObserverList bindingObservers;
221 // update all delayed properties
222 auto start = data;
223 while (data) {
224 for (qsizetype i = 0; i < data->used; ++i)
225 data->evaluateBindings(bindingObservers, i, status);
226 data = data->next;
227 }
228 // notify all delayed notifications from binding evaluation
229 for (const QBindingObserverPtr &observer: bindingObservers) {
230 QPropertyBindingPrivate *binding = observer.binding();
231 binding->notifyNonRecursive();
232 }
233 // do the same for properties which only have observers
234 data = start;
235 while (data) {
236 for (qsizetype i = 0; i < data->used; ++i)
237 data->notify(i);
238 delete std::exchange(data, data->next);
239 }
240}
241
277// check everything stored in QPropertyBindingPrivate's union is trivially destructible
278// (though the compiler would also complain if that weren't the case)
279static_assert(std::is_trivially_destructible_v<QPropertyBindingSourceLocation>);
280static_assert(std::is_trivially_destructible_v<std::byte[sizeof(QPropertyBindingSourceLocation)]>);
281
283{
284 if (firstObserver)
285 firstObserver.unlink();
286 if (vtable->size)
287 vtable->destroy(reinterpret_cast<std::byte *>(this)
289}
290
292 for (size_t i = 0; i < qMin(dependencyObserverCount, inlineDependencyObservers.size()); ++i) {
293 QPropertyObserverPointer p{&inlineDependencyObservers[i]};
294 p.unlink_fast();
295 }
296 if (heapObservers)
297 heapObservers->clear();
299}
300
302{
304 if (!heapObservers)
305 heapObservers.reset(new std::vector<QPropertyObserver>());
306 return {&heapObservers->emplace_back()};
307}
308
316
318{
319 if (!status)
320 status = &bindingStatus;
321 return evaluateRecursive_inline(bindingObservers, status);
322}
323
325{
327 for (auto &&bindingObserver: bindingObservers) {
328 bindingObserver.binding()->notifyNonRecursive();
329 }
330}
331
333{
334 if (!pendingNotify)
335 return Delayed;
336 pendingNotify = false;
337 Q_ASSERT(!updating);
338 updating = true;
339 if (firstObserver) {
340 firstObserver.noSelfDependencies(this);
341 firstObserver.notify(propertyDataPtr);
342 }
343 if (hasStaticObserver)
345 updating = false;
346 return Sent;
347}
348
355
372{
373 std::byte *mem = new std::byte[QPropertyBindingPrivate::getSizeEnsuringAlignment() + vtable->size]();
374 d = new(mem) QPropertyBindingPrivate(metaType, vtable, std::move(location));
376}
377
388
396
405
417
425
432
440{
441 return !d;
442}
443
450{
451 if (!d)
452 return QPropertyBindingError();
453 return static_cast<QPropertyBindingPrivate *>(d.get())->bindingError();
454}
455
461{
462 if (!d)
463 return QMetaType();
464 return static_cast<QPropertyBindingPrivate *>(d.get())->valueMetaType();
465}
466
468{
471 proxyData()->originalBindingData = nullptr;
472 for (auto observer = d.firstObserver(); observer;) {
473 auto next = observer.nextObserver();
474 observer.unlink();
475 observer = next;
476 }
477 if (auto binding = d.binding())
479}
480
482 QUntypedPropertyData *propertyDataPtr,
483 QPropertyObserverCallback staticObserverCallback,
485{
487 QPropertyBindingPrivatePtr newBinding = binding.d;
488
491
492 auto &data = d_ref();
493 if (auto *existingBinding = d.binding()) {
494 if (existingBinding == newBinding.data())
495 return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
496 if (existingBinding->isUpdating()) {
497 existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
498 return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
499 }
500 oldBinding = QPropertyBindingPrivatePtr(existingBinding);
501 observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
502 static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
503 data = 0;
504 } else {
505 observer = d.firstObserver();
506 }
507
508 if (newBinding) {
509 newBinding.data()->addRef();
510 data = reinterpret_cast<quintptr>(newBinding.data());
511 data |= BindingBit;
512 auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
513 newBindingRaw->setProperty(propertyDataPtr);
514 if (observer)
515 newBindingRaw->prependObserver(observer);
516 newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
517
518 PendingBindingObserverList bindingObservers;
519 newBindingRaw->evaluateRecursive(bindingObservers);
520 newBindingRaw->notifyNonRecursive(bindingObservers);
521 } else if (observer) {
522 d.setObservers(observer.ptr);
523 } else {
524 data = 0;
525 }
526
527 if (oldBinding)
528 static_cast<QPropertyBindingPrivate *>(oldBinding.data())->detachFromProperty();
529
530 return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
531}
532
537
539 : binding(binding)
540{
541 Q_ASSERT(status);
542 QBindingStatus *s = status;
543 // store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in
544 // the destructor (as these come with a non zero cost)
545 currentState = &s->currentlyEvaluatingBinding;
547 *currentState = this;
549}
550
553{
554 // store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in
555 // the destructor (as these come with a non zero cost)
558 *currentState = this;
559
560 currentlyEvaluatingBindingList = &bindingStatus.currentlyEvaluatingBinding;
563}
564
566{
567 auto currentState = bindingStatus.currentlyEvaluatingBinding ;
568 return currentState ? currentState->binding : nullptr;
569}
570
571// ### Unused, kept for BC with 6.0
575
576void QPropertyBindingData::removeBinding_helper()
577{
579
580 auto *existingBinding = d.binding();
581 Q_ASSERT(existingBinding);
582 if (existingBinding->isSticky()) {
583 return;
584 }
585
586 auto observer = existingBinding->takeObservers();
587 d_ref() = 0;
588 if (observer)
589 d.setObservers(observer.ptr);
590 existingBinding->unlinkAndDeref();
591}
592
594{
595 auto currentState = bindingStatus.currentlyEvaluatingBinding;
596 if (!currentState)
597 return;
598 registerWithCurrentlyEvaluatingBinding_helper(currentState);
599}
600
601
602void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentState) const
603{
605
606 if (currentState->alreadyCaptureProperties.contains(this))
607 return;
608 else
609 currentState->alreadyCaptureProperties.push_back(this);
610
611 QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
613 dependencyObserver.setBindingToNotify_unsafe(currentState->binding);
614 d.addObserver(dependencyObserver.ptr);
615}
616
618{
619 notifyObservers(propertyDataPtr, nullptr);
620}
621
623{
625 return;
627
628 PendingBindingObserverList bindingObservers;
629 if (QPropertyObserverPointer observer = d.firstObserver()) {
630 if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
631 /* evaluateBindings() can trash the observers. We need to re-fetch here.
632 "this" might also no longer be valid in case we have a QObjectBindableProperty
633 and consequently d isn't either (this happens when binding evaluation has
634 caused the binding storage to resize.
635 If storage is nullptr, then there is no dynamically resizable storage,
636 and we cannot run into the issue.
637 */
638 if (storage)
639 d = QPropertyBindingDataPointer {storage->bindingData(propertyDataPtr)};
640 if (QPropertyObserverPointer observer = d.firstObserver())
641 observer.notify(propertyDataPtr);
642 for (auto &&bindingObserver: bindingObservers)
643 bindingObserver.binding()->notifyNonRecursive();
644 }
645 }
646}
647
648QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper
649(
652 PendingBindingObserverList &bindingObservers) const
653{
654#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
655 QBindingStatus *status = storage ? storage->bindingStatus : nullptr;
656 if (!status || status->threadId != QThread::currentThreadId())
657 status = &bindingStatus;
658#else
660 QBindingStatus *status = &bindingStatus;
661#endif
662 if (QPropertyDelayedNotifications *delay = status->groupUpdateData) {
663 delay->addProperty(this, propertyDataPtr);
664 return Delayed;
665 }
666
667 observer.evaluateBindings(bindingObservers, status);
668 return Evaluated;
669}
670
671
677
678#if QT_DEPRECATED_SINCE(6, 6)
680{
682 aliasData = data;
683 next.setTag(ObserverIsAlias);
685}
686#endif
687
691{
693 QPropertyBindingDataPointer propPrivate{&property};
694 d.observeProperty(propPrivate);
695}
696
702
704{
705 binding = std::exchange(other.binding, {});
706 next = std::exchange(other.next, {});
707 prev = std::exchange(other.prev, {});
708 if (next)
709 next->prev = &next;
710 if (prev)
711 prev.setPointer(this);
712}
713
715{
716 if (this == &other)
717 return *this;
718
720 d.unlink();
721 binding = nullptr;
722
723 binding = std::exchange(other.binding, {});
724 next = std::exchange(other.next, {});
725 prev = std::exchange(other.prev, {});
726 if (next)
727 next->prev = &next;
728 if (prev)
729 prev.setPointer(this);
730
731 return *this;
732}
733
755
765
779#ifndef QT_NO_DEBUG
781{
782 auto observer = const_cast<QPropertyObserver*>(ptr);
783 // See also comment in notify()
784 while (observer) {
786 if (observer->binding == binding) {
787 qCritical("Property depends on itself!");
788 break;
789 }
790
791 observer = observer->next.data();
792 }
793
794}
795#endif
796
798{
799 Q_ASSERT(status);
800 auto observer = const_cast<QPropertyObserver*>(ptr);
801 // See also comment in notify()
802 while (observer) {
803 QPropertyObserver *next = observer->next.data();
804
806 auto bindingToEvaluate = observer->binding;
807 QPropertyObserverNodeProtector protector(observer);
808 QBindingObserverPtr bindingObserver(observer); // binding must not be gone after evaluateRecursive_inline
809 if (bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status))
810 bindingObservers.push_back(std::move(bindingObserver));
811 next = protector.next();
812 }
813
814 observer = next;
815 }
816}
817
819{
820 if (ptr->prev)
821 unlink();
822 property.addObserver(ptr);
823}
824
875
881{
882 if (type != NoError) {
884 d->type = type;
885 d->description = description;
886 }
887}
888
896
905
914
920{
921 d = std::move(other.d);
922 return *this;
923}
924
931
943
949{
950 if (!d)
951 return QString();
952 return d->description;
953}
954
1128
1145
1219
1229
1240
2155{
2156 size_t size = 0;
2157 size_t used = 0;
2158 // Pair[] pairs;
2159};
2160
2162{
2163 // This class basically implements a simple and fast hash map to store bindings for a QObject
2164 // The reason that we're not using QHash is that QPropertyBindingData can not be copied, only
2165 // moved. That doesn't work well together with an implicitly shared class.
2171 static_assert(alignof(Pair) == alignof(void *));
2172 static_assert(alignof(size_t) == alignof(void *));
2173
2175
2176 static inline Pair *pairs(QBindingStorageData *dd)
2177 {
2178 Q_ASSERT(dd);
2179 return reinterpret_cast<Pair *>(dd + 1);
2180 }
2181 void reallocate(size_t newSize)
2182 {
2183 Q_ASSERT(!d || newSize > d->size);
2184 size_t allocSize = sizeof(QBindingStorageData) + newSize*sizeof(Pair);
2185 void *nd = malloc(allocSize);
2186 memset(nd, 0, allocSize);
2187 QBindingStorageData *newData = new (nd) QBindingStorageData;
2188 newData->size = newSize;
2189 if (!d) {
2190 d = newData;
2191 return;
2192 }
2193 newData->used = d->used;
2194 Pair *p = pairs(d);
2195 for (size_t i = 0; i < d->size; ++i, ++p) {
2196 if (p->data) {
2197 Pair *pp = pairs(newData);
2198 Q_ASSERT(newData->size && (newData->size & (newData->size - 1)) == 0); // size is a power of two
2199 size_t index = qHash(p->data) & (newData->size - 1);
2200 while (pp[index].data) {
2201 ++index;
2202 if (index == newData->size)
2203 index = 0;
2204 }
2205 new (pp + index) Pair{p->data, QPropertyBindingData(std::move(p->bindingData))};
2206 }
2207 }
2208 // data has been moved, no need to call destructors on old Pairs
2209 free(d);
2210 d = newData;
2211 }
2212
2214
2216 {
2217 Q_ASSERT(d);
2218 Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two
2219 size_t index = qHash(data) & (d->size - 1);
2220 Pair *p = pairs(d);
2221 while (p[index].data) {
2222 if (p[index].data == data)
2223 return &p[index].bindingData;
2224 ++index;
2225 if (index == d->size)
2226 index = 0;
2227 }
2228 return nullptr;
2229 }
2231 {
2232 if (!d) {
2233 if (!create)
2234 return nullptr;
2235 reallocate(8);
2236 }
2237 else if (d->used*2 >= d->size)
2238 reallocate(d->size*2);
2239 Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two
2240 size_t index = qHash(data) & (d->size - 1);
2241 Pair *p = pairs(d);
2242 while (p[index].data) {
2243 if (p[index].data == data)
2244 return &p[index].bindingData;
2245 ++index;
2246 if (index == d->size)
2247 index = 0;
2248 }
2249 if (!create)
2250 return nullptr;
2251 ++d->used;
2252 new (p + index) Pair{data, QPropertyBindingData()};
2253 return &p[index].bindingData;
2254 }
2255
2256 void destroy()
2257 {
2258 if (!d)
2259 return;
2260 Pair *p = pairs(d);
2261 for (size_t i = 0; i < d->size; ++i) {
2262 if (p->data)
2263 p->~Pair();
2264 ++p;
2265 }
2266 free(d);
2267 }
2268};
2269
2283{
2284 bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
2286}
2287
2292
2293void QBindingStorage::reinitAfterThreadMove()
2294{
2295 bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
2296 Q_ASSERT(bindingStatus);
2297}
2298
2299void QBindingStorage::clear()
2300{
2302 d = nullptr;
2303 bindingStatus = nullptr;
2304}
2305
2306void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
2307{
2308 Q_ASSERT(bindingStatus);
2309 // Use ::bindingStatus to get the binding from TLS. This is required, so that reads from
2310 // another thread do not register as dependencies
2311 QtPrivate::BindingEvaluationState *currentBinding;
2312#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
2313 const bool threadMatches = (QThread::currentThreadId() == bindingStatus->threadId);
2314 if (Q_LIKELY(threadMatches))
2315 currentBinding = bindingStatus->currentlyEvaluatingBinding;
2316 else
2317 currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
2318#else
2319 currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
2320#endif
2321 QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
2322 if (!currentBinding)
2323 return;
2324 auto storage = QBindingStoragePrivate(d).get(dd, true);
2325 if (!storage)
2326 return;
2327 storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
2328}
2329
2330
2331QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
2332{
2333 return QBindingStoragePrivate(d).get(data);
2334}
2335
2340
2341QPropertyBindingData *QBindingStorage::bindingData_helper(QUntypedPropertyData *data, bool create)
2342{
2344}
2345
2346
2347namespace QtPrivate {
2348
2349
2354
2356{
2357 auto ret = bindingStatus.currentlyEvaluatingBinding;
2358 bindingStatus.currentlyEvaluatingBinding = nullptr;
2359 return ret;
2360}
2361
2363{
2364 bindingStatus.currentlyEvaluatingBinding = status;
2365}
2366
2376{
2377 return bindingStatus.currentlyEvaluatingBinding != nullptr;
2378}
2379
2381{
2382 // Accessing bindingStatus is expensive because it's thread-local. Do it only once.
2383 if (const auto current = bindingStatus.currentCompatProperty)
2384 return current->property == property;
2385 return false;
2386}
2387
2388namespace BindableWarnings {
2389
2391{
2392 switch (reason) {
2394 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2395 << "The QBindable does not allow interaction with the binding.";
2396 break;
2398 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2399 << "The QBindable is read-only.";
2400 break;
2401 default:
2403 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2404 << "The QBindable is invalid.";
2405 break;
2406 }
2407}
2408
2410{
2411 qCWarning(lcQPropertyBinding) << "setBinding: Could not set binding as the property expects it to be of type"
2412 << actual.name()
2413 << "but got" << expected.name() << "instead.";
2414}
2415
2416} // namespace BindableWarnings end
2417
2423
2424namespace PropertyAdaptorSlotObjectHelpers {
2426{
2427 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2429 auto mt = adaptor->metaProperty().metaType();
2430 mt.destruct(value);
2431 mt.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
2432}
2433
2435{
2436 auto adaptor = static_cast<QtPrivate::QPropertyAdaptorSlotObject *>(d);
2437 adaptor->bindingData().removeBinding();
2438 adaptor->metaProperty().write(adaptor->object(),
2439 QVariant(adaptor->metaProperty().metaType(), value));
2440}
2441
2443{
2444 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2445 return QUntypedPropertyBinding(adaptor->bindingData().binding());
2446}
2447
2450 void *value)
2451{
2452 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2453 type.destruct(value);
2454 type.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
2455 if (binding.vtable->call(type, temp, binding.functor)) {
2456 adaptor->metaProperty().write(adaptor->object(), QVariant(type, value));
2457 return true;
2458 }
2459 return false;
2460}
2461
2464{
2465 auto adaptor = static_cast<QPropertyAdaptorSlotObject *>(d);
2466 return adaptor->bindingData().setBinding(binding, d, nullptr, wrapper);
2467}
2468
2470{
2471 observer->setSource(static_cast<const QPropertyAdaptorSlotObject *>(d)->bindingData());
2472}
2473}
2474
2475QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty &p)
2476 : QSlotObjectBase(&impl), obj(o), metaProperty_(p)
2477{
2478}
2479
2480#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
2481void QPropertyAdaptorSlotObject::impl(int which, QSlotObjectBase *this_, QObject *r, void **a,
2482 bool *ret)
2483#else
2484void QPropertyAdaptorSlotObject::impl(QSlotObjectBase *this_, QObject *r, void **a, int which,
2485 bool *ret)
2486#endif
2487{
2488 auto self = static_cast<QPropertyAdaptorSlotObject *>(this_);
2489 switch (which) {
2490 case Destroy:
2491 delete self;
2492 break;
2493 case Call:
2494 if (!self->bindingData_.hasBinding())
2495 self->bindingData_.notifyObservers(self);
2496 break;
2497 case Compare:
2498 case NumOperations:
2499 Q_UNUSED(r);
2500 Q_UNUSED(a);
2501 Q_UNUSED(ret);
2502 break;
2503 }
2504}
2505
2506} // namespace QtPrivate end
2507
2510 : iface(i)
2511{
2512 if (!obj)
2513 return;
2514
2515 if (!metaProperty.isValid()) {
2516 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property is not valid";
2517 return;
2518 }
2519
2520 if (metaProperty.isBindable()) {
2521 *this = metaProperty.bindable(obj);
2522 return;
2523 }
2524
2525 if (!metaProperty.hasNotifySignal()) {
2526 qCWarning(lcQPropertyBinding)
2527 << "QUntypedBindable: Property" << metaProperty.name() << "has no notify signal";
2528 return;
2529 }
2530
2531 auto metatype = iface->metaType();
2532 if (metaProperty.metaType() != metatype) {
2533 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
2534 << "of type" << metaProperty.metaType().name()
2535 << "does not match requested type" << metatype.name();
2536 return;
2537 }
2538
2539 // Test for name pointer equality proves it's exactly the same property
2540 if (obj->metaObject()->property(metaProperty.propertyIndex()).name() != metaProperty.name()) {
2541 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
2542 << "does not belong to this object";
2543 return;
2544 }
2545
2546 // Get existing binding data if it exists
2547 auto adaptor = QObjectPrivate::get(obj)->getPropertyAdaptorSlotObject(metaProperty);
2548
2549 if (!adaptor) {
2550 adaptor = new QPropertyAdaptorSlotObject(obj, metaProperty);
2551
2552 auto c = QObjectPrivate::connect(obj, metaProperty.notifySignalIndex(), obj, adaptor,
2554 Q_ASSERT(c);
2555 }
2556
2557 data = adaptor;
2558}
2559
2563 obj,
2564 [=]() -> QMetaProperty {
2565 if (!obj)
2566 return {};
2567 auto propertyIndex = obj->metaObject()->indexOfProperty(property);
2568 if (propertyIndex < 0) {
2569 qCWarning(lcQPropertyBinding)
2570 << "QUntypedBindable: No property named" << property;
2571 return {};
2572 }
2573 return obj->metaObject()->property(propertyIndex);
2574 }(),
2575 i)
2576{
2577}
2578
\inmodule QtCore
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1218
const QBindingStatus * status(QtPrivate::QBindingStatusAccessToken) const
\inmodule QtCore
bool isBindable() const
bool write(QObject *obj, const QVariant &value) const
Writes value as the property's value to the given object.
QMetaType metaType() const
int notifySignalIndex() const
int propertyIndex() const
const char * name() const
Returns this property's name.
bool isValid() const
Returns true if this property is valid (readable); otherwise returns false.
QUntypedBindable bindable(QObject *object) const
bool hasNotifySignal() const
Returns true if this property has a corresponding change notify signal; otherwise returns false.
\inmodule QtCore
Definition qmetatype.h:341
constexpr const char * name() const
Definition qmetatype.h:2680
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:150
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
QPropertyBindingError::Type type
\inmodule QtCore
Definition qproperty.h:131
~QPropertyBindingError()
Destroys the QPropertyBindingError.
QString description() const
Returns a descriptive error message for the QPropertyBindingError if it has been set.
Type
This enum specifies which error occurred.
Definition qproperty.h:133
QPropertyBindingError & operator=(const QPropertyBindingError &other)
Copies other to this QPropertyBindingError.
Type type() const
Returns the type of the QPropertyBindingError.
QPropertyBindingError()
Default constructs QPropertyBindingError.
T * get() const noexcept
Q_CORE_EXPORT void destroyAndFreeMemory()
Definition qproperty.cpp:21
T * data() const noexcept
void reset(T *ptr=nullptr) noexcept
Definition qproperty.cpp:26
static QPropertyBindingPrivate * currentlyEvaluatingBinding()
QPropertyObserverPointer allocateDependencyObserver_slow()
static constexpr size_t getSizeEnsuringAlignment()
void setProperty(QUntypedPropertyData *propertyPtr)
bool evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status=nullptr)
static void destroyAndFreeMemory(QPropertyBindingPrivate *priv)
void notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
Q_ALWAYS_INLINE QPropertyObserverPointer allocateDependencyObserver()
bool Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
QtPrivate::QPropertyObserverCallback staticObserverCallback
NotificationState notifyNonRecursive()
QUntypedPropertyData * propertyDataPtr
void(*)(QPropertyObserver *, QUntypedPropertyData *) ChangeHandler
Definition qproperty.h:236
void setSource(const Property &property)
Definition qproperty.h:267
QPropertyObserver & operator=(QPropertyObserver &&other) noexcept
constexpr QPropertyObserver()=default
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION
Definition qthread.h:149
\inmodule QtCore
Definition qproperty.h:679
const QtPrivate::QBindableInterface * iface
Definition qproperty.h:683
constexpr QUntypedBindable()=default
Default-constructs a QUntypedBindable.
QMetaType valueMetaType() const
Returns the meta-type of the binding.
QUntypedPropertyBinding()
Constructs a null QUntypedPropertyBinding.
QPropertyBindingError error() const
Returns the error state of the binding.
bool isNull() const
Returns true if the QUntypedPropertyBinding is null.
~QUntypedPropertyBinding()
Destroys the QUntypedPropertyBinding.
friend class QPropertyBindingPrivate
Definition qproperty.h:186
QUntypedPropertyBinding & operator=(const QUntypedPropertyBinding &other)
Copy-assigns other to this QUntypedPropertyBinding.
\inmodule QtCore
Definition qvariant.h:65
const QMetaProperty & metaProperty() const
const QPropertyBindingData & bindingData() const
void registerWithCurrentlyEvaluatingBinding(QtPrivate::BindingEvaluationState *currentBinding) const
QPropertyBindingPrivate * binding() const
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding, QUntypedPropertyData *propertyDataPtr, QPropertyObserverCallback staticObserverCallback=nullptr, QPropertyBindingWrapper bindingWrapper=nullptr)
static constexpr quintptr DelayedNotificationBit
void evaluateIfDirty(const QUntypedPropertyData *) const
void notifyObservers(QUntypedPropertyData *propertyDataPtr) const
static constexpr quintptr BindingBit
void registerWithCurrentlyEvaluatingBinding() const
QJSValue expected
Definition qjsengine.cpp:12
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
void printMetaTypeMismatch(QMetaType actual, QMetaType expected)
void printUnsuitableBindableWarning(QAnyStringView prefix, BindableWarnings::Reason reason)
void getter(const QUntypedPropertyData *d, void *value)
void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer)
QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding, QPropertyBindingWrapper wrapper)
void setter(QUntypedPropertyData *d, const void *value)
bool bindingWrapper(QMetaType type, QUntypedPropertyData *d, QtPrivate::QPropertyBindingFunction binding, QUntypedPropertyData *temp, void *value)
QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d)
\macro QT_NO_KEYWORDS >
bool isAnyBindingEvaluating()
void(*)(QUntypedPropertyData *) QPropertyObserverCallback
bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction) QPropertyBindingWrapper
void initBindingStatusThreadId()
BindingEvaluationState * suspendCurrentBindingStatus()
bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
Q_AUTOTEST_EXPORT QBindingStatus * getBindingStatus(QBindingStatusAccessToken)
void restoreBindingStatus(BindingEvaluationState *status)
Q_CORE_EXPORT void beginPropertyUpdateGroup()
@ DirectConnection
Q_CORE_EXPORT void endPropertyUpdateGroup()
QString self
Definition language.cpp:58
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_DEPRECATED
#define Q_LIKELY(x)
#define QT_WARNING_PUSH
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
size_t qHash(const QFileSystemWatcherPathKey &key, size_t seed=0)
#define qCritical
Definition qlogging.h:167
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
return ret
static ControlElement< T > * ptr(QWidget *widget)
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLint location
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLuint start
GLint ref
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLfloat GLfloat p
[1]
static Q_CONSTINIT thread_local QBindingStatus bindingStatus
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
@ NoError
Definition main.cpp:34
#define Q_UNUSED(x)
size_t quintptr
Definition qtypes.h:167
ptrdiff_t qsizetype
Definition qtypes.h:165
const char property[13]
Definition qwizard.cpp:101
if(qFloatDistance(a, b)<(1<< 7))
[0]
QStorageInfo storage
[1]
QSharedPointer< T > other(t)
[5]
view create()
Qt::HANDLE threadId
QtPrivate::CompatPropertySafePoint * currentCompatProperty
QPropertyDelayedNotifications * groupUpdateData
QPropertyBindingData bindingData
QUntypedPropertyData * data
static Pair * pairs(QBindingStorageData *dd)
QBindingStorageData *& d
QBindingStoragePrivate(QBindingStorageData *&_d)
void reallocate(size_t newSize)
QPropertyBindingData * get(QUntypedPropertyData *data, bool create)
QPropertyBindingData * get(const QUntypedPropertyData *data)
static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
void Q_ALWAYS_INLINE addObserver(QPropertyObserver *observer)
Definition qproperty.cpp:38
const QtPrivate::QPropertyBindingData * ptr
Definition qproperty_p.h:70
QPropertyBindingPrivate * binding() const
Definition qproperty_p.h:72
QPropertyObserverPointer firstObserver() const
QPropertyDelayedNotifications * next
Definition qproperty.cpp:73
static constexpr auto PageSize
Definition qproperty.cpp:71
void notify(qsizetype index)
void addProperty(const QPropertyBindingData *bindingData, QUntypedPropertyData *propertyData)
Definition qproperty.cpp:89
QPropertyProxyBindingData delayedProperties[size]
Definition qproperty.cpp:78
void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status)
static constexpr qsizetype size
Definition qproperty.cpp:77
void noSelfDependencies(QPropertyBindingPrivate *binding)
void notify(QUntypedPropertyData *propertyDataPtr)
QPropertyBindingPrivate * binding() const
void evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
void observeProperty(QPropertyBindingDataPointer property)
QPropertyObserver * ptr
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler)
const QtPrivate::QPropertyBindingData * originalBindingData
QPropertyBindingPrivate * binding
QVarLengthArray< const QPropertyBindingData *, 8 > alreadyCaptureProperties
BindingEvaluationState(QPropertyBindingPrivate *binding, QBindingStatus *status)
BindingEvaluationState * previousState
BindingEvaluationState ** currentState
QtPrivate::BindingEvaluationState ** currentlyEvaluatingBindingList
CompatPropertySafePoint * previousState
CompatPropertySafePoint ** currentState
QtPrivate::BindingEvaluationState * bindingState
Q_CORE_EXPORT CompatPropertySafePoint(QBindingStatus *status, QUntypedPropertyData *property)
const QtPrivate::BindingFunctionVTable * vtable
Definition moc.h:23
void wrapper()