Qt 6.x
The Qt SDK
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
22{
24}
25
27{
28 if (ptr != d) {
29 if (ptr)
30 ptr->ref++;
31 auto *old = std::exchange(d, ptr);
32 if (old && (--old->ref == 0))
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{
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
310{
312 propertyDataPtr = nullptr;
313 if (--ref == 0)
315}
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
385 : d(std::move(other.d))
386{
387}
388
393 : d(other.d)
394{
395}
396
401{
402 d = other.d;
403 return *this;
404}
405
413{
414 d = std::move(other.d);
415 return *this;
416}
417
422 : d(priv)
423{
424}
425
430{
431}
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
534{
536}
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
563}
564
566{
567 auto currentState = bindingStatus.currentlyEvaluatingBinding ;
568 return currentState ? currentState->binding : nullptr;
569}
570
571// ### Unused, kept for BC with 6.0
573{
574}
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
673{
675 d.setChangeHandler(changeHandler);
676}
677
678#if QT_DEPRECATED_SINCE(6, 6)
680{
681 aliasData = data;
682 next.setTag(ObserverIsAlias);
683}
684#endif
685
689{
691 QPropertyBindingDataPointer propPrivate{&property};
692 d.observeProperty(propPrivate);
693}
694
696{
698 d.unlink();
699}
700
702{
703 binding = std::exchange(other.binding, {});
704 next = std::exchange(other.next, {});
705 prev = std::exchange(other.prev, {});
706 if (next)
707 next->prev = &next;
708 if (prev)
709 prev.setPointer(this);
710}
711
713{
714 if (this == &other)
715 return *this;
716
718 d.unlink();
719 binding = nullptr;
720
721 binding = std::exchange(other.binding, {});
722 next = std::exchange(other.next, {});
723 prev = std::exchange(other.prev, {});
724 if (next)
725 next->prev = &next;
726 if (prev)
727 prev.setPointer(this);
728
729 return *this;
730}
731
748{
750 ptr->changeHandler = changeHandler;
752}
753
755{
757 ptr->binding = binding;
759}
760
766{
768 ptr->binding = binding;
769}
770
784#ifndef QT_NO_DEBUG
786{
787 auto observer = const_cast<QPropertyObserver*>(ptr);
788 // See also comment in notify()
789 while (observer) {
791 if (observer->binding == binding) {
792 qCritical("Property depends on itself!");
793 break;
794 }
795
796 observer = observer->next.data();
797 }
798
799}
800#endif
801
803{
804 Q_ASSERT(status);
805 auto observer = const_cast<QPropertyObserver*>(ptr);
806 // See also comment in notify()
807 while (observer) {
808 QPropertyObserver *next = observer->next.data();
809
811 auto bindingToEvaluate = observer->binding;
812 QPropertyObserverNodeProtector protector(observer);
813 QBindingObserverPtr bindingObserver(observer); // binding must not be gone after evaluateRecursive_inline
814 if (bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status))
815 bindingObservers.push_back(std::move(bindingObserver));
816 next = protector.next();
817 }
818
819 observer = next;
820 }
821}
822
824{
825 if (ptr->prev)
826 unlink();
827 property.addObserver(ptr);
828}
829
878{
879}
880
886{
887 if (type != NoError) {
889 d->type = type;
890 d->description = description;
891 }
892}
893
898 : d(other.d)
899{
900}
901
906{
907 d = other.d;
908 return *this;
909}
910
916 : d(std::move(other.d))
917{
918}
919
925{
926 d = std::move(other.d);
927 return *this;
928}
929
934{
935}
936
943{
944 if (!d)
946 return d->type;
947}
948
954{
955 if (!d)
956 return QString();
957 return d->description;
958}
959
1133
1150
1224
1234
1245
2162{
2163 size_t size = 0;
2164 size_t used = 0;
2165 // Pair[] pairs;
2166};
2167
2169{
2170 // This class basically implements a simple and fast hash map to store bindings for a QObject
2171 // The reason that we're not using QHash is that QPropertyBindingData can not be copied, only
2172 // moved. That doesn't work well together with an implicitly shared class.
2173 struct Pair
2174 {
2177 };
2178 static_assert(alignof(Pair) == alignof(void *));
2179 static_assert(alignof(size_t) == alignof(void *));
2180
2182
2183 static inline Pair *pairs(QBindingStorageData *dd)
2184 {
2185 Q_ASSERT(dd);
2186 return reinterpret_cast<Pair *>(dd + 1);
2187 }
2188 void reallocate(size_t newSize)
2189 {
2190 Q_ASSERT(!d || newSize > d->size);
2191 size_t allocSize = sizeof(QBindingStorageData) + newSize*sizeof(Pair);
2192 void *nd = malloc(allocSize);
2193 memset(nd, 0, allocSize);
2194 QBindingStorageData *newData = new (nd) QBindingStorageData;
2195 newData->size = newSize;
2196 if (!d) {
2197 d = newData;
2198 return;
2199 }
2200 newData->used = d->used;
2201 Pair *p = pairs(d);
2202 for (size_t i = 0; i < d->size; ++i, ++p) {
2203 if (p->data) {
2204 Pair *pp = pairs(newData);
2205 Q_ASSERT(newData->size && (newData->size & (newData->size - 1)) == 0); // size is a power of two
2206 size_t index = qHash(p->data) & (newData->size - 1);
2207 while (pp[index].data) {
2208 ++index;
2209 if (index == newData->size)
2210 index = 0;
2211 }
2212 new (pp + index) Pair{p->data, QPropertyBindingData(std::move(p->bindingData))};
2213 }
2214 }
2215 // data has been moved, no need to call destructors on old Pairs
2216 free(d);
2217 d = newData;
2218 }
2219
2221
2223 {
2224 Q_ASSERT(d);
2225 Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two
2226 size_t index = qHash(data) & (d->size - 1);
2227 Pair *p = pairs(d);
2228 while (p[index].data) {
2229 if (p[index].data == data)
2230 return &p[index].bindingData;
2231 ++index;
2232 if (index == d->size)
2233 index = 0;
2234 }
2235 return nullptr;
2236 }
2238 {
2239 if (!d) {
2240 if (!create)
2241 return nullptr;
2242 reallocate(8);
2243 }
2244 else if (d->used*2 >= d->size)
2245 reallocate(d->size*2);
2246 Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two
2247 size_t index = qHash(data) & (d->size - 1);
2248 Pair *p = pairs(d);
2249 while (p[index].data) {
2250 if (p[index].data == data)
2251 return &p[index].bindingData;
2252 ++index;
2253 if (index == d->size)
2254 index = 0;
2255 }
2256 if (!create)
2257 return nullptr;
2258 ++d->used;
2259 new (p + index) Pair{data, QPropertyBindingData()};
2260 return &p[index].bindingData;
2261 }
2262
2263 void destroy()
2264 {
2265 if (!d)
2266 return;
2267 Pair *p = pairs(d);
2268 for (size_t i = 0; i < d->size; ++i) {
2269 if (p->data)
2270 p->~Pair();
2271 ++p;
2272 }
2273 free(d);
2274 }
2275};
2276
2290{
2291 bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
2292 Q_ASSERT(bindingStatus);
2293}
2294
2296{
2298}
2299
2300void QBindingStorage::reinitAfterThreadMove()
2301{
2302 bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
2303 Q_ASSERT(bindingStatus);
2304}
2305
2306void QBindingStorage::clear()
2307{
2309 d = nullptr;
2310 bindingStatus = nullptr;
2311}
2312
2313void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
2314{
2315 Q_ASSERT(bindingStatus);
2316 // Use ::bindingStatus to get the binding from TLS. This is required, so that reads from
2317 // another thread do not register as dependencies
2318 QtPrivate::BindingEvaluationState *currentBinding;
2319#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
2320 const bool threadMatches = (QThread::currentThreadId() == bindingStatus->threadId);
2321 if (Q_LIKELY(threadMatches))
2322 currentBinding = bindingStatus->currentlyEvaluatingBinding;
2323 else
2324 currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
2325#else
2326 currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
2327#endif
2328 QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
2329 if (!currentBinding)
2330 return;
2331 auto storage = QBindingStoragePrivate(d).get(dd, true);
2332 if (!storage)
2333 return;
2334 storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
2335}
2336
2337
2338QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
2339{
2340 return QBindingStoragePrivate(d).get(data);
2341}
2342
2344{
2345 return bindingStatus;
2346}
2347
2348QPropertyBindingData *QBindingStorage::bindingData_helper(QUntypedPropertyData *data, bool create)
2349{
2351}
2352
2353
2354namespace QtPrivate {
2355
2356
2358{
2360}
2361
2363{
2366 return ret;
2367}
2368
2370{
2372}
2373
2383{
2384 return bindingStatus.currentlyEvaluatingBinding != nullptr;
2385}
2386
2388{
2389 // Accessing bindingStatus is expensive because it's thread-local. Do it only once.
2390 if (const auto current = bindingStatus.currentCompatProperty)
2391 return current->property == property;
2392 return false;
2393}
2394
2395namespace BindableWarnings {
2396
2398{
2399 switch (reason) {
2401 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2402 << "The QBindable does not allow interaction with the binding.";
2403 break;
2405 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2406 << "The QBindable is read-only.";
2407 break;
2408 default:
2410 qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
2411 << "The QBindable is invalid.";
2412 break;
2413 }
2414}
2415
2417{
2418 qCWarning(lcQPropertyBinding) << "setBinding: Could not set binding as the property expects it to be of type"
2419 << actual.name()
2420 << "but got" << expected.name() << "instead.";
2421}
2422
2423} // namespace BindableWarnings end
2424
2430
2431namespace PropertyAdaptorSlotObjectHelpers {
2433{
2434 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2436 auto mt = adaptor->metaProperty().metaType();
2437 mt.destruct(value);
2438 mt.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
2439}
2440
2442{
2443 auto adaptor = static_cast<QtPrivate::QPropertyAdaptorSlotObject *>(d);
2444 adaptor->bindingData().removeBinding();
2445 adaptor->metaProperty().write(adaptor->object(),
2446 QVariant(adaptor->metaProperty().metaType(), value));
2447}
2448
2450{
2451 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2452 return QUntypedPropertyBinding(adaptor->bindingData().binding());
2453}
2454
2457 void *value)
2458{
2459 auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
2460 type.destruct(value);
2461 type.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
2462 if (binding.vtable->call(type, temp, binding.functor)) {
2463 adaptor->metaProperty().write(adaptor->object(), QVariant(type, value));
2464 return true;
2465 }
2466 return false;
2467}
2468
2471{
2472 auto adaptor = static_cast<QPropertyAdaptorSlotObject *>(d);
2473 return adaptor->bindingData().setBinding(binding, d, nullptr, wrapper);
2474}
2475
2477{
2478 observer->setSource(static_cast<const QPropertyAdaptorSlotObject *>(d)->bindingData());
2479}
2480}
2481
2482QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty &p)
2483 : QSlotObjectBase(&impl), obj(o), metaProperty_(p)
2484{
2485}
2486
2487#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
2488void QPropertyAdaptorSlotObject::impl(int which, QSlotObjectBase *this_, QObject *r, void **a,
2489 bool *ret)
2490#else
2491void QPropertyAdaptorSlotObject::impl(QSlotObjectBase *this_, QObject *r, void **a, int which,
2492 bool *ret)
2493#endif
2494{
2495 auto self = static_cast<QPropertyAdaptorSlotObject *>(this_);
2496 switch (which) {
2497 case Destroy:
2498 delete self;
2499 break;
2500 case Call:
2501 if (!self->bindingData_.hasBinding())
2502 self->bindingData_.notifyObservers(self);
2503 break;
2504 case Compare:
2505 case NumOperations:
2506 Q_UNUSED(r);
2507 Q_UNUSED(a);
2508 Q_UNUSED(ret);
2509 break;
2510 }
2511}
2512
2513} // namespace QtPrivate end
2514
2517 : iface(i)
2518{
2519 if (!obj)
2520 return;
2521
2522 if (!metaProperty.isValid()) {
2523 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property is not valid";
2524 return;
2525 }
2526
2527 if (metaProperty.isBindable()) {
2528 *this = metaProperty.bindable(obj);
2529 return;
2530 }
2531
2532 if (!metaProperty.hasNotifySignal()) {
2533 qCWarning(lcQPropertyBinding)
2534 << "QUntypedBindable: Property" << metaProperty.name() << "has no notify signal";
2535 return;
2536 }
2537
2538 auto metatype = iface->metaType();
2539 if (metaProperty.metaType() != metatype) {
2540 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
2541 << "of type" << metaProperty.metaType().name()
2542 << "does not match requested type" << metatype.name();
2543 return;
2544 }
2545
2546 // Test for name pointer equality proves it's exactly the same property
2547 if (obj->metaObject()->property(metaProperty.propertyIndex()).name() != metaProperty.name()) {
2548 qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
2549 << "does not belong to this object";
2550 return;
2551 }
2552
2553 // Get existing binding data if it exists
2554 auto adaptor = QObjectPrivate::get(obj)->getPropertyAdaptorSlotObject(metaProperty);
2555
2556 if (!adaptor) {
2557 adaptor = new QPropertyAdaptorSlotObject(obj, metaProperty);
2558
2559 auto c = QObjectPrivate::connect(obj, metaProperty.notifySignalIndex(), obj, adaptor,
2561 Q_ASSERT(c);
2562 }
2563
2564 data = adaptor;
2565}
2566
2570 obj,
2571 [=]() -> QMetaProperty {
2572 if (!obj)
2573 return {};
2574 auto propertyIndex = obj->metaObject()->indexOfProperty(property);
2575 if (propertyIndex < 0) {
2576 qCWarning(lcQPropertyBinding)
2577 << "QUntypedBindable: No property named" << property;
2578 return {};
2579 }
2580 return obj->metaObject()->property(propertyIndex);
2581 }(),
2582 i)
2583{
2584}
2585
\inmodule QtCore
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1071
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:320
constexpr const char * name() const
Definition qmetatype.h:2650
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:153
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:298
QtPrivate::QPropertyAdaptorSlotObject * getPropertyAdaptorSlotObject(const QMetaProperty &property)
Definition qobject.cpp:5399
\inmodule QtCore
Definition qobject.h:90
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
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION
Definition qthread.h:154
\inmodule QtCore
Definition qproperty.h:677
const QtPrivate::QBindableInterface * iface
Definition qproperty.h:681
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.
void push_back(const T &t)
\inmodule QtCore
Definition qvariant.h:64
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_NAMESPACE
bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction) QPropertyBindingWrapper
bool isAnyBindingEvaluating()
void initBindingStatusThreadId()
BindingEvaluationState * suspendCurrentBindingStatus()
bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
Q_AUTOTEST_EXPORT QBindingStatus * getBindingStatus(QBindingStatusAccessToken)
void(*)(QUntypedPropertyData *) QPropertyObserverCallback
void restoreBindingStatus(BindingEvaluationState *status)
Q_CORE_EXPORT void beginPropertyUpdateGroup()
@ DirectConnection
Q_CORE_EXPORT void endPropertyUpdateGroup()
QString self
Definition language.cpp:57
#define Q_LIKELY(x)
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:163
#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]
GLenum type
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLint ref
GLhandleARB obj
[2]
const GLubyte * c
GLdouble s
[6]
Definition qopenglext.h:235
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:72
ptrdiff_t qsizetype
Definition qtypes.h:70
QT_END_NAMESPACE typedef QT_PREPEND_NAMESPACE(quintptr) WId
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
QtPrivate::BindingEvaluationState * currentlyEvaluatingBinding
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:69
QPropertyBindingPrivate * binding() const
Definition qproperty_p.h:71
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
QPropertyObserver * next() const
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)
void setBindingToNotify(QPropertyBindingPrivate *binding)
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:24
void wrapper()
virtual HRESULT STDMETHODCALLTYPE Compare(__RPC__in_opt ITextRangeProvider *range, __RPC__out BOOL *pRetVal)=0