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#ifndef QPROPERTY_H
5#define QPROPERTY_H
6
7#include <QtCore/qglobal.h>
8#include <QtCore/qshareddata.h>
9#include <QtCore/qstring.h>
10#include <QtCore/qttypetraits.h>
11#include <QtCore/qbindingstorage.h>
12
13#include <type_traits>
14
15#include <QtCore/qpropertyprivate.h>
16
17#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_QDOC)
18#include <source_location>
19#if defined(__cpp_lib_source_location)
20#define QT_SOURCE_LOCATION_NAMESPACE std
21#define QT_PROPERTY_COLLECT_BINDING_LOCATION
22#if defined(Q_CC_MSVC)
23/* MSVC runs into an issue with constexpr with source location (error C7595)
24 so use the factory function as a workaround */
25# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation::fromStdSourceLocation(std::source_location::current())
26#else
27/* some versions of gcc in turn run into
28 expression ‘std::source_location::current()’ is not a constant expression
29 so don't use the workaround there */
30# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current())
31#endif
32#endif
33#endif
34
35#if __has_include(<experimental/source_location>) && !defined(Q_QDOC)
36#include <experimental/source_location>
37#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION)
38#if defined(__cpp_lib_experimental_source_location)
39#define QT_SOURCE_LOCATION_NAMESPACE std::experimental
40#define QT_PROPERTY_COLLECT_BINDING_LOCATION
41#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::experimental::source_location::current())
42#endif // defined(__cpp_lib_experimental_source_location)
43#endif
44#endif
45
46#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION)
47#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation()
48#endif
49
50QT_BEGIN_NAMESPACE
51
52namespace Qt {
53Q_CORE_EXPORT void beginPropertyUpdateGroup();
54Q_CORE_EXPORT void endPropertyUpdateGroup();
55}
56
57class QScopedPropertyUpdateGroup
58{
59 Q_DISABLE_COPY_MOVE(QScopedPropertyUpdateGroup)
60public:
61 Q_NODISCARD_CTOR
62 QScopedPropertyUpdateGroup()
63 { Qt::beginPropertyUpdateGroup(); }
64 ~QScopedPropertyUpdateGroup() noexcept(false)
65 { Qt::endPropertyUpdateGroup(); }
66};
67
68template <typename T>
69class QPropertyData : public QUntypedPropertyData
70{
71protected:
72 mutable T val = T();
73private:
74 class DisableRValueRefs {};
75protected:
76 static constexpr bool UseReferences = !(std::is_arithmetic_v<T> || std::is_enum_v<T> || std::is_pointer_v<T>);
77public:
78 using value_type = T;
79 using parameter_type = std::conditional_t<UseReferences, const T &, T>;
80 using rvalue_ref = typename std::conditional_t<UseReferences, T &&, DisableRValueRefs>;
81 using arrow_operator_result = std::conditional_t<std::is_pointer_v<T>, const T &,
82 std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, const T &, void>>;
83
84 QPropertyData() = default;
85 QPropertyData(parameter_type t) : val(t) {}
86 QPropertyData(rvalue_ref t) : val(std::move(t)) {}
87 ~QPropertyData() = default;
88
89 parameter_type valueBypassingBindings() const { return val; }
90 void setValueBypassingBindings(parameter_type v) { val = v; }
91 void setValueBypassingBindings(rvalue_ref v) { val = std::move(v); }
92};
93
94// ### Qt 7: un-export
95struct Q_CORE_EXPORT QPropertyBindingSourceLocation
96{
97 const char *fileName = nullptr;
98 const char *functionName = nullptr;
99 quint32 line = 0;
100 quint32 column = 0;
101 QPropertyBindingSourceLocation() = default;
102#ifdef __cpp_lib_source_location
103 constexpr QPropertyBindingSourceLocation(const std::source_location &cppLocation)
104 {
105 fileName = cppLocation.file_name();
106 functionName = cppLocation.function_name();
107 line = cppLocation.line();
108 column = cppLocation.column();
109 }
110 QT_POST_CXX17_API_IN_EXPORTED_CLASS
111 static consteval QPropertyBindingSourceLocation
112 fromStdSourceLocation(const std::source_location &cppLocation)
113 {
114 return cppLocation;
115 }
116#endif
117#ifdef __cpp_lib_experimental_source_location
118 constexpr QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation)
119 {
120 fileName = cppLocation.file_name();
121 functionName = cppLocation.function_name();
122 line = cppLocation.line();
123 column = cppLocation.column();
124 }
125#endif
126};
127
128template <typename Functor> class QPropertyChangeHandler;
129class QPropertyBindingErrorPrivate;
130
131class Q_CORE_EXPORT QPropertyBindingError
132{
133public:
134 enum Type {
135 NoError,
136 BindingLoop,
137 EvaluationError,
138 UnknownError
139 };
140
141 QPropertyBindingError();
142 QPropertyBindingError(Type type, const QString &description = QString());
143
144 QPropertyBindingError(const QPropertyBindingError &other);
145 QPropertyBindingError &operator=(const QPropertyBindingError &other);
146 QPropertyBindingError(QPropertyBindingError &&other);
147 QPropertyBindingError &operator=(QPropertyBindingError &&other);
148 ~QPropertyBindingError();
149
150 bool hasError() const { return d.get() != nullptr; }
151 Type type() const;
152 QString description() const;
153
154private:
155 QSharedDataPointer<QPropertyBindingErrorPrivate> d;
156};
157
158class Q_CORE_EXPORT QUntypedPropertyBinding
159{
160public:
161 // writes binding result into dataPtr
162 using BindingFunctionVTable = QtPrivate::BindingFunctionVTable;
163
164 QUntypedPropertyBinding();
165 QUntypedPropertyBinding(QMetaType metaType, const BindingFunctionVTable *vtable, void *function, const QPropertyBindingSourceLocation &location);
166
167 template<typename Functor>
168 QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location)
169 : QUntypedPropertyBinding(metaType, &QtPrivate::bindingFunctionVTable<std::remove_reference_t<Functor>>, &f, location)
170 {}
171
172 QUntypedPropertyBinding(QUntypedPropertyBinding &&other);
173 QUntypedPropertyBinding(const QUntypedPropertyBinding &other);
174 QUntypedPropertyBinding &operator=(const QUntypedPropertyBinding &other);
175 QUntypedPropertyBinding &operator=(QUntypedPropertyBinding &&other);
176 ~QUntypedPropertyBinding();
177
178 bool isNull() const;
179
180 QPropertyBindingError error() const;
181
182 QMetaType valueMetaType() const;
183
184 explicit QUntypedPropertyBinding(QPropertyBindingPrivate *priv);
185private:
186 friend class QtPrivate::QPropertyBindingData;
187 friend class QPropertyBindingPrivate;
188 template <typename> friend class QPropertyBinding;
189 QPropertyBindingPrivatePtr d;
190};
191
192template <typename PropertyType>
193class QPropertyBinding : public QUntypedPropertyBinding
194{
195
196public:
197 QPropertyBinding() = default;
198
199 template<typename Functor>
200 QPropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location)
201 : QUntypedPropertyBinding(QMetaType::fromType<PropertyType>(), &QtPrivate::bindingFunctionVTable<std::remove_reference_t<Functor>, PropertyType>, &f, location)
202 {}
203
204
205 // Internal
206 explicit QPropertyBinding(const QUntypedPropertyBinding &binding)
207 : QUntypedPropertyBinding(binding)
208 {}
209};
210
211namespace Qt {
212 template <typename Functor>
213 auto makePropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
214 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
215 {
216 return QPropertyBinding<std::invoke_result_t<Functor>>(std::forward<Functor>(f), location);
217 }
218}
219
220struct QPropertyObserverPrivate;
221struct QPropertyObserverPointer;
222class QPropertyObserver;
223
224class QPropertyObserverBase
225{
226public:
227 // Internal
228 enum ObserverTag {
229 ObserverNotifiesBinding, // observer was installed to notify bindings that obsverved property changed
230 ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change
231 ObserverIsPlaceholder, // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers.
232#if QT_DEPRECATED_SINCE(6, 6)
233 ObserverIsAlias QT_DEPRECATED_VERSION_X_6_6("Use QProperty and add a binding to the target.")
234#endif
235 };
236protected:
237 using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
238
239private:
240 friend struct QPropertyDelayedNotifications;
241 friend struct QPropertyObserverNodeProtector;
242 friend class QPropertyObserver;
243 friend struct QPropertyObserverPointer;
244 friend struct QPropertyBindingDataPointer;
245 friend class QPropertyBindingPrivate;
246
247 QTaggedPointer<QPropertyObserver, ObserverTag> next;
248 // prev is a pointer to the "next" element within the previous node, or to the "firstObserverPtr" if it is the
249 // first node.
250 QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
251
252 union {
253 QPropertyBindingPrivate *binding = nullptr;
254 ChangeHandler changeHandler;
255 QUntypedPropertyData *aliasData;
256 };
257};
258
259class Q_CORE_EXPORT QPropertyObserver : public QPropertyObserverBase
260{
261public:
262 constexpr QPropertyObserver() = default;
263 QPropertyObserver(QPropertyObserver &&other) noexcept;
264 QPropertyObserver &operator=(QPropertyObserver &&other) noexcept;
265 ~QPropertyObserver();
266
267 template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
268 void setSource(const Property &property)
269 { setSource(property.bindingData()); }
270 void setSource(const QtPrivate::QPropertyBindingData &property);
271
272protected:
273 QPropertyObserver(ChangeHandler changeHandler);
274#if QT_DEPRECATED_SINCE(6, 6)
275 QT_DEPRECATED_VERSION_X_6_6("This constructor was only meant for internal use. Use QProperty and add a binding to the target.")
276 QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr);
277#endif
278
279 QUntypedPropertyData *aliasedProperty() const
280 {
281 return aliasData;
282 }
283
284private:
285
286 QPropertyObserver(const QPropertyObserver &) = delete;
287 QPropertyObserver &operator=(const QPropertyObserver &) = delete;
288
289};
290
291template <typename Functor>
292class QPropertyChangeHandler : public QPropertyObserver
293{
294 Functor m_handler;
295public:
296 Q_NODISCARD_CTOR
297 QPropertyChangeHandler(Functor handler)
298 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
299 auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
300 This->m_handler();
301 })
302 , m_handler(handler)
303 {
304 }
305
306 template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
307 Q_NODISCARD_CTOR
308 QPropertyChangeHandler(const Property &property, Functor handler)
309 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
310 auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
311 This->m_handler();
312 })
313 , m_handler(handler)
314 {
315 setSource(property);
316 }
317};
318
319class QPropertyNotifier : public QPropertyObserver
320{
321 std::function<void()> m_handler;
322public:
323 Q_NODISCARD_CTOR
324 QPropertyNotifier() = default;
325 template<typename Functor>
326 Q_NODISCARD_CTOR
327 QPropertyNotifier(Functor handler)
328 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
329 auto This = static_cast<QPropertyNotifier *>(self);
330 This->m_handler();
331 })
332 , m_handler(handler)
333 {
334 }
335
336 template <typename Functor, typename Property,
337 QtPrivate::IsUntypedPropertyData<Property> = true>
338 Q_NODISCARD_CTOR
339 QPropertyNotifier(const Property &property, Functor handler)
340 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
341 auto This = static_cast<QPropertyNotifier *>(self);
342 This->m_handler();
343 })
344 , m_handler(handler)
345 {
346 setSource(property);
347 }
348};
349
350template <typename T>
351class QProperty : public QPropertyData<T>
352{
353 QtPrivate::QPropertyBindingData d;
354 bool is_equal(const T &v)
355 {
356 if constexpr (QTypeTraits::has_operator_equal_v<T>) {
357 if (v == this->val)
358 return true;
359 }
360 return false;
361 }
362
363public:
364 using value_type = typename QPropertyData<T>::value_type;
365 using parameter_type = typename QPropertyData<T>::parameter_type;
366 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
367 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
368
369 QProperty() = default;
370 explicit QProperty(parameter_type initialValue) : QPropertyData<T>(initialValue) {}
371 explicit QProperty(rvalue_ref initialValue) : QPropertyData<T>(std::move(initialValue)) {}
372 explicit QProperty(const QPropertyBinding<T> &binding)
373 : QProperty()
374 { setBinding(binding); }
375#ifndef Q_QDOC
376 template <typename Functor>
377 explicit QProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
378 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
379 : QProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
380 {}
381#else
382 template <typename Functor>
383 explicit QProperty(Functor &&f);
384#endif
385 ~QProperty() = default;
386
387 parameter_type value() const
388 {
389 d.registerWithCurrentlyEvaluatingBinding();
390 return this->val;
391 }
392
393 arrow_operator_result operator->() const
394 {
395 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
396 return value();
397 } else if constexpr (std::is_pointer_v<T>) {
398 value();
399 return this->val;
400 } else {
401 return;
402 }
403 }
404
405 parameter_type operator*() const
406 {
407 return value();
408 }
409
410 operator parameter_type() const
411 {
412 return value();
413 }
414
415 void setValue(rvalue_ref newValue)
416 {
417 d.removeBinding();
418 if (is_equal(v: newValue))
419 return;
420 this->val = std::move(newValue);
421 notify();
422 }
423
424 void setValue(parameter_type newValue)
425 {
426 d.removeBinding();
427 if (is_equal(v: newValue))
428 return;
429 this->val = newValue;
430 notify();
431 }
432
433 QProperty<T> &operator=(rvalue_ref newValue)
434 {
435 setValue(std::move(newValue));
436 return *this;
437 }
438
439 QProperty<T> &operator=(parameter_type newValue)
440 {
441 setValue(newValue);
442 return *this;
443 }
444
445 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
446 {
447 return QPropertyBinding<T>(d.setBinding(newBinding, propertyDataPtr: this));
448 }
449
450 bool setBinding(const QUntypedPropertyBinding &newBinding)
451 {
452 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
453 return false;
454 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
455 return true;
456 }
457
458#ifndef Q_QDOC
459 template <typename Functor>
460 QPropertyBinding<T> setBinding(Functor &&f,
461 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
462 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
463 {
464 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
465 }
466#else
467 template <typename Functor>
468 QPropertyBinding<T> setBinding(Functor f);
469#endif
470
471 bool hasBinding() const { return d.hasBinding(); }
472
473 QPropertyBinding<T> binding() const
474 {
475 return QPropertyBinding<T>(QUntypedPropertyBinding(d.binding()));
476 }
477
478 QPropertyBinding<T> takeBinding()
479 {
480 return QPropertyBinding<T>(d.setBinding(newBinding: QUntypedPropertyBinding(), propertyDataPtr: this));
481 }
482
483 template<typename Functor>
484 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
485 {
486 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
487 return QPropertyChangeHandler<Functor>(*this, f);
488 }
489
490 template<typename Functor>
491 QPropertyChangeHandler<Functor> subscribe(Functor f)
492 {
493 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
494 f();
495 return onValueChanged(f);
496 }
497
498 template<typename Functor>
499 QPropertyNotifier addNotifier(Functor f)
500 {
501 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
502 return QPropertyNotifier(*this, f);
503 }
504
505 const QtPrivate::QPropertyBindingData &bindingData() const { return d; }
506private:
507 void notify()
508 {
509 d.notifyObservers(this);
510 }
511
512 Q_DISABLE_COPY_MOVE(QProperty)
513};
514
515namespace Qt {
516 template <typename PropertyType>
517 QPropertyBinding<PropertyType> makePropertyBinding(const QProperty<PropertyType> &otherProperty,
518 const QPropertyBindingSourceLocation &location =
519 QT_PROPERTY_DEFAULT_BINDING_LOCATION)
520 {
521 return Qt::makePropertyBinding([&otherProperty]() -> PropertyType { return otherProperty; }, location);
522 }
523}
524
525
526namespace QtPrivate
527{
528
529struct QBindableInterface
530{
531 using Getter = void (*)(const QUntypedPropertyData *d, void *value);
532 using Setter = void (*)(QUntypedPropertyData *d, const void *value);
533 using BindingGetter = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d);
534 using BindingSetter = QUntypedPropertyBinding (*)(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding);
535 using MakeBinding = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location);
536 using SetObserver = void (*)(const QUntypedPropertyData *d, QPropertyObserver *observer);
537 using GetMetaType = QMetaType (*)();
538 Getter getter;
539 Setter setter;
540 BindingGetter getBinding;
541 BindingSetter setBinding;
542 MakeBinding makeBinding;
543 SetObserver setObserver;
544 GetMetaType metaType;
545
546 static constexpr quintptr MetaTypeAccessorFlag = 0x1;
547};
548
549template<typename Property, typename = void>
550class QBindableInterfaceForProperty
551{
552 using T = typename Property::value_type;
553public:
554 // interface for computed properties. Those do not have a binding()/setBinding() method, but one can
555 // install observers on them.
556 static constexpr QBindableInterface iface = {
557 [](const QUntypedPropertyData *d, void *value) -> void
558 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
559 nullptr,
560 nullptr,
561 nullptr,
562 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
563 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
564 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
565 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
566 []() { return QMetaType::fromType<T>(); }
567 };
568};
569
570template<typename Property>
571class QBindableInterfaceForProperty<const Property, std::void_t<decltype(std::declval<Property>().binding())>>
572{
573 using T = typename Property::value_type;
574public:
575 // A bindable created from a const property results in a read-only interface, too.
576 static constexpr QBindableInterface iface = {
577
578 [](const QUntypedPropertyData *d, void *value) -> void
579 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
580 /*setter=*/nullptr,
581 [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
582 { return static_cast<const Property *>(d)->binding(); },
583 /*setBinding=*/nullptr,
584 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
585 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
586 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
587 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
588 []() { return QMetaType::fromType<T>(); }
589 };
590};
591
592template<typename Property>
593class QBindableInterfaceForProperty<Property, std::void_t<decltype(std::declval<Property>().binding())>>
594{
595 using T = typename Property::value_type;
596public:
597 static constexpr QBindableInterface iface = {
598 [](const QUntypedPropertyData *d, void *value) -> void
599 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
600 [](QUntypedPropertyData *d, const void *value) -> void
601 { static_cast<Property *>(d)->setValue(*static_cast<const T*>(value)); },
602 [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
603 { return static_cast<const Property *>(d)->binding(); },
604 [](QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) -> QUntypedPropertyBinding
605 { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
606 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
607 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
608 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
609 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
610 []() { return QMetaType::fromType<T>(); }
611 };
612};
613
614}
615
616namespace QtPrivate {
617// used in Q(Untyped)Bindable to print warnings about various binding errors
618namespace BindableWarnings {
619enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface };
620Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason);
621Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected);
622}
623
624namespace PropertyAdaptorSlotObjectHelpers {
625Q_CORE_EXPORT void getter(const QUntypedPropertyData *d, void *value);
626Q_CORE_EXPORT void setter(QUntypedPropertyData *d, const void *value);
627Q_CORE_EXPORT QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d);
628Q_CORE_EXPORT bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
629 QtPrivate::QPropertyBindingFunction binding,
630 QUntypedPropertyData *temp, void *value);
631Q_CORE_EXPORT QUntypedPropertyBinding setBinding(QUntypedPropertyData *d,
632 const QUntypedPropertyBinding &binding,
633 QPropertyBindingWrapper wrapper);
634Q_CORE_EXPORT void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer);
635
636template<typename T>
637bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
638 QtPrivate::QPropertyBindingFunction binding)
639{
640 struct Data : QPropertyData<T>
641 {
642 void *data() { return &this->val; }
643 } temp;
644 return bindingWrapper(type, d, binding, &temp, temp.data());
645}
646
647template<typename T>
648QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding)
649{
650 return setBinding(d, binding, &bindingWrapper<T>);
651}
652
653template<typename T>
654QUntypedPropertyBinding makeBinding(const QUntypedPropertyData *d,
655 const QPropertyBindingSourceLocation &location)
656{
657 return Qt::makePropertyBinding(
658 [d]() -> T {
659 T r;
660 getter(d, &r);
661 return r;
662 },
663 location);
664}
665
666template<class T>
667inline constexpr QBindableInterface iface = {
668 &getter,
669 &setter,
670 &getBinding,
671 &setBinding<T>,
672 &makeBinding<T>,
673 &setObserver,
674 &QMetaType::fromType<T>,
675};
676}
677}
678
679class QUntypedBindable
680{
681 friend struct QUntypedBindablePrivate; // allows access to internal data
682protected:
683 QUntypedPropertyData *data = nullptr;
684 const QtPrivate::QBindableInterface *iface = nullptr;
685 constexpr QUntypedBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
686 : data(d), iface(i)
687 {}
688
689 Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i);
690 Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i);
691
692public:
693 constexpr QUntypedBindable() = default;
694 template<typename Property>
695 QUntypedBindable(Property *p)
696 : data(const_cast<std::remove_cv_t<Property> *>(p)),
697 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
698 { Q_ASSERT(data && iface); }
699
700 bool isValid() const { return data != nullptr; }
701 bool isBindable() const { return iface && iface->getBinding; }
702 bool isReadOnly() const { return !(iface && iface->setBinding && iface->setObserver); }
703
704 QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
705 {
706 return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
707 }
708
709 QUntypedPropertyBinding takeBinding()
710 {
711 if (!iface)
712 return QUntypedPropertyBinding {};
713 // We do not have a dedicated takeBinding function pointer in the interface
714 // therefore we synthesize takeBinding by retrieving the binding with binding
715 // and calling setBinding with a default constructed QUntypedPropertyBinding
716 // afterwards.
717 if (!(iface->getBinding && iface->setBinding))
718 return QUntypedPropertyBinding {};
719 QUntypedPropertyBinding binding = iface->getBinding(data);
720 iface->setBinding(data, QUntypedPropertyBinding{});
721 return binding;
722 }
723
724 void observe(QPropertyObserver *observer) const
725 {
726 if (iface)
727 iface->setObserver(data, observer);
728#ifndef QT_NO_DEBUG
729 else
730 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "observe:",
731 reason: QtPrivate::BindableWarnings::InvalidInterface);
732#endif
733 }
734
735 template<typename Functor>
736 QPropertyChangeHandler<Functor> onValueChanged(Functor f) const
737 {
738 QPropertyChangeHandler<Functor> handler(f);
739 observe(observer: &handler);
740 return handler;
741 }
742
743 template<typename Functor>
744 QPropertyChangeHandler<Functor> subscribe(Functor f) const
745 {
746 f();
747 return onValueChanged(f);
748 }
749
750 template<typename Functor>
751 QPropertyNotifier addNotifier(Functor f)
752 {
753 QPropertyNotifier handler(f);
754 observe(observer: &handler);
755 return handler;
756 }
757
758 QUntypedPropertyBinding binding() const
759 {
760 if (!isBindable()) {
761#ifndef QT_NO_DEBUG
762 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "binding: ",
763 reason: QtPrivate::BindableWarnings::NonBindableInterface);
764#endif
765 return QUntypedPropertyBinding();
766 }
767 return iface->getBinding(data);
768 }
769 bool setBinding(const QUntypedPropertyBinding &binding)
770 {
771 if (isReadOnly()) {
772#ifndef QT_NO_DEBUG
773 const auto errorType = iface ? QtPrivate::BindableWarnings::ReadOnlyInterface :
774 QtPrivate::BindableWarnings::InvalidInterface;
775 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding: Could not set binding via bindable interface.", reason: errorType);
776#endif
777 return false;
778 }
779 if (!binding.isNull() && binding.valueMetaType() != metaType()) {
780#ifndef QT_NO_DEBUG
781 QtPrivate::BindableWarnings::printMetaTypeMismatch(actual: metaType(), expected: binding.valueMetaType());
782#endif
783 return false;
784 }
785 iface->setBinding(data, binding);
786 return true;
787 }
788 bool hasBinding() const
789 {
790 return !binding().isNull();
791 }
792
793 QMetaType metaType() const
794 {
795 if (!(iface && data))
796 return QMetaType();
797 if (iface->metaType)
798 return iface->metaType();
799 // ### Qt 7: Change the metatype function to take data as its argument
800 // special casing for QML's proxy bindable: allow multiplexing in the getter
801 // function to retrieve the metatype from data
802 Q_ASSERT(iface->getter);
803 QMetaType result;
804 iface->getter(data, reinterpret_cast<void *>(quintptr(&result) | QtPrivate::QBindableInterface::MetaTypeAccessorFlag));
805 return result;
806 }
807
808};
809
810template<typename T>
811class QBindable : public QUntypedBindable
812{
813 template<typename U>
814 friend class QPropertyAlias;
815 constexpr QBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
816 : QUntypedBindable(d, i)
817 {}
818public:
819 using QUntypedBindable::QUntypedBindable;
820 explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b)
821 {
822 if (iface && metaType() != QMetaType::fromType<T>()) {
823 data = nullptr;
824 iface = nullptr;
825 }
826 }
827
828 explicit QBindable(QObject *obj, const QMetaProperty &property)
829 : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
830
831 explicit QBindable(QObject *obj, const char *property)
832 : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
833
834 QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
835 {
836 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
837 }
838 QPropertyBinding<T> binding() const
839 {
840 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding());
841 }
842
843 QPropertyBinding<T> takeBinding()
844 {
845 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::takeBinding());
846 }
847
848 using QUntypedBindable::setBinding;
849 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
850 {
851 Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == metaType());
852
853 if (iface && iface->setBinding)
854 return static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding));
855#ifndef QT_NO_DEBUG
856 if (!iface)
857 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding", reason: QtPrivate::BindableWarnings::InvalidInterface);
858 else
859 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding: Could not set binding via bindable interface.", reason: QtPrivate::BindableWarnings::ReadOnlyInterface);
860#endif
861 return QPropertyBinding<T>();
862 }
863#ifndef Q_QDOC
864 template <typename Functor>
865 QPropertyBinding<T> setBinding(Functor &&f,
866 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
867 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
868 {
869 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
870 }
871#else
872 template <typename Functor>
873 QPropertyBinding<T> setBinding(Functor f);
874#endif
875
876 T value() const
877 {
878 if (iface) {
879 T result;
880 iface->getter(data, &result);
881 return result;
882 }
883 return T{};
884 }
885
886 void setValue(const T &value)
887 {
888 if (iface && iface->setter)
889 iface->setter(data, &value);
890 }
891};
892
893#if QT_DEPRECATED_SINCE(6, 6)
894template<typename T>
895class QT_DEPRECATED_VERSION_X_6_6("Class was only meant for internal use, use a QProperty and add a binding to the target")
896QPropertyAlias : public QPropertyObserver
897{
898 Q_DISABLE_COPY_MOVE(QPropertyAlias)
899 const QtPrivate::QBindableInterface *iface = nullptr;
900
901public:
902 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
903 QPropertyAlias(QProperty<T> *property)
904 : QPropertyObserver(property),
905 iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface)
906 {
907 if (iface)
908 iface->setObserver(aliasedProperty(), this);
909 }
910
911 template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
912 QPropertyAlias(Property *property)
913 : QPropertyObserver(property),
914 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
915 {
916 if (iface)
917 iface->setObserver(aliasedProperty(), this);
918 }
919
920 QPropertyAlias(QPropertyAlias<T> *alias)
921 : QPropertyObserver(alias->aliasedProperty()),
922 iface(alias->iface)
923 {
924 if (iface)
925 iface->setObserver(aliasedProperty(), this);
926 }
927
928 QPropertyAlias(const QBindable<T> &property)
929 : QPropertyObserver(property.data),
930 iface(property.iface)
931 {
932 if (iface)
933 iface->setObserver(aliasedProperty(), this);
934 }
935
936 T value() const
937 {
938 T t = T();
939 if (auto *p = aliasedProperty())
940 iface->getter(p, &t);
941 return t;
942 }
943
944 operator T() const { return value(); }
945
946 void setValue(const T &newValue)
947 {
948 if (auto *p = aliasedProperty())
949 iface->setter(p, &newValue);
950 }
951
952 QPropertyAlias<T> &operator=(const T &newValue)
953 {
954 setValue(newValue);
955 return *this;
956 }
957
958 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
959 {
960 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
961 }
962
963 bool setBinding(const QUntypedPropertyBinding &newBinding)
964 {
965 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
966 }
967
968#ifndef Q_QDOC
969 template <typename Functor>
970 QPropertyBinding<T> setBinding(Functor &&f,
971 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
972 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
973 {
974 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
975 }
976#else
977 template <typename Functor>
978 QPropertyBinding<T> setBinding(Functor f);
979#endif
980
981 bool hasBinding() const
982 {
983 return QBindable<T>(aliasedProperty(), iface).hasBinding();
984 }
985
986 QPropertyBinding<T> binding() const
987 {
988 return QBindable<T>(aliasedProperty(), iface).binding();
989 }
990
991 QPropertyBinding<T> takeBinding()
992 {
993 return QBindable<T>(aliasedProperty(), iface).takeBinding();
994 }
995
996 template<typename Functor>
997 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
998 {
999 return QBindable<T>(aliasedProperty(), iface).onValueChanged(f);
1000 }
1001
1002 template<typename Functor>
1003 QPropertyChangeHandler<Functor> subscribe(Functor f)
1004 {
1005 return QBindable<T>(aliasedProperty(), iface).subscribe(f);
1006 }
1007
1008 template<typename Functor>
1009 QPropertyNotifier addNotifier(Functor f)
1010 {
1011 return QBindable<T>(aliasedProperty(), iface).addNotifier(f);
1012 }
1013
1014 bool isValid() const
1015 {
1016 return aliasedProperty() != nullptr;
1017 }
1018 QT_WARNING_POP
1019};
1020#endif // QT_DEPRECATED_SINCE(6, 6)
1021
1022template<typename Class, typename T, auto Offset, auto Signal = nullptr>
1023class QObjectBindableProperty : public QPropertyData<T>
1024{
1025 using ThisType = QObjectBindableProperty<Class, T, Offset, Signal>;
1026 static bool constexpr HasSignal = !std::is_same_v<decltype(Signal), std::nullptr_t>;
1027 using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
1028 Class *owner()
1029 {
1030 char *that = reinterpret_cast<char *>(this);
1031 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1032 }
1033 const Class *owner() const
1034 {
1035 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
1036 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1037 }
1038 static void signalCallBack(QUntypedPropertyData *o)
1039 {
1040 QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o);
1041 if constexpr (HasSignal) {
1042 if constexpr (SignalTakesValue::value)
1043 (that->owner()->*Signal)(that->valueBypassingBindings());
1044 else
1045 (that->owner()->*Signal)();
1046 }
1047 }
1048public:
1049 using value_type = typename QPropertyData<T>::value_type;
1050 using parameter_type = typename QPropertyData<T>::parameter_type;
1051 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
1052 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
1053
1054 QObjectBindableProperty() = default;
1055 explicit QObjectBindableProperty(const T &initialValue) : QPropertyData<T>(initialValue) {}
1056 explicit QObjectBindableProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue)) {}
1057 explicit QObjectBindableProperty(const QPropertyBinding<T> &binding)
1058 : QObjectBindableProperty()
1059 { setBinding(binding); }
1060#ifndef Q_QDOC
1061 template <typename Functor>
1062 explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
1063 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
1064 : QObjectBindableProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
1065 {}
1066#else
1067 template <typename Functor>
1068 explicit QObjectBindableProperty(Functor &&f);
1069#endif
1070
1071 parameter_type value() const
1072 {
1073 qGetBindingStorage(owner())->registerDependency(this);
1074 return this->val;
1075 }
1076
1077 arrow_operator_result operator->() const
1078 {
1079 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
1080 return value();
1081 } else if constexpr (std::is_pointer_v<T>) {
1082 value();
1083 return this->val;
1084 } else {
1085 return;
1086 }
1087 }
1088
1089 parameter_type operator*() const
1090 {
1091 return value();
1092 }
1093
1094 operator parameter_type() const
1095 {
1096 return value();
1097 }
1098
1099 void setValue(parameter_type t)
1100 {
1101 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1102 if (bd)
1103 bd->removeBinding();
1104 if (this->val == t)
1105 return;
1106 this->val = t;
1107 notify(bd);
1108 }
1109
1110 void notify() {
1111 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1112 notify(bd);
1113 }
1114
1115 void setValue(rvalue_ref t)
1116 {
1117 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1118 if (bd)
1119 bd->removeBinding();
1120 if (this->val == t)
1121 return;
1122 this->val = std::move(t);
1123 notify(bd);
1124 }
1125
1126 QObjectBindableProperty &operator=(rvalue_ref newValue)
1127 {
1128 setValue(std::move(newValue));
1129 return *this;
1130 }
1131
1132 QObjectBindableProperty &operator=(parameter_type newValue)
1133 {
1134 setValue(newValue);
1135 return *this;
1136 }
1137
1138 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
1139 {
1140 QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
1141 QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, propertyDataPtr: this, staticObserverCallback: HasSignal ? &signalCallBack : nullptr));
1142 return static_cast<QPropertyBinding<T> &>(oldBinding);
1143 }
1144
1145 bool setBinding(const QUntypedPropertyBinding &newBinding)
1146 {
1147 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
1148 return false;
1149 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
1150 return true;
1151 }
1152
1153#ifndef Q_QDOC
1154 template <typename Functor>
1155 QPropertyBinding<T> setBinding(Functor &&f,
1156 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
1157 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
1158 {
1159 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
1160 }
1161#else
1162 template <typename Functor>
1163 QPropertyBinding<T> setBinding(Functor f);
1164#endif
1165
1166 bool hasBinding() const
1167 {
1168 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1169 return bd && bd->binding() != nullptr;
1170 }
1171
1172 QPropertyBinding<T> binding() const
1173 {
1174 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1175 return static_cast<QPropertyBinding<T> &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr));
1176 }
1177
1178 QPropertyBinding<T> takeBinding()
1179 {
1180 return setBinding(QPropertyBinding<T>());
1181 }
1182
1183 template<typename Functor>
1184 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1185 {
1186 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1187 return QPropertyChangeHandler<Functor>(*this, f);
1188 }
1189
1190 template<typename Functor>
1191 QPropertyChangeHandler<Functor> subscribe(Functor f)
1192 {
1193 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1194 f();
1195 return onValueChanged(f);
1196 }
1197
1198 template<typename Functor>
1199 QPropertyNotifier addNotifier(Functor f)
1200 {
1201 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1202 return QPropertyNotifier(*this, f);
1203 }
1204
1205 const QtPrivate::QPropertyBindingData &bindingData() const
1206 {
1207 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1208 return *storage->bindingData(const_cast<ThisType *>(this), true);
1209 }
1210private:
1211 void notify(const QtPrivate::QPropertyBindingData *binding)
1212 {
1213 if (binding)
1214 binding->notifyObservers(this, qGetBindingStorage(owner()));
1215 if constexpr (HasSignal) {
1216 if constexpr (SignalTakesValue::value)
1217 (owner()->*Signal)(this->valueBypassingBindings());
1218 else
1219 (owner()->*Signal)();
1220 }
1221 }
1222};
1223
1224#define QT_OBJECT_BINDABLE_PROPERTY_3(Class, Type, name) \
1225 static constexpr size_t _qt_property_##name##_offset() { \
1226 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1227 return offsetof(Class, name); \
1228 QT_WARNING_POP \
1229 } \
1230 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name;
1231
1232#define QT_OBJECT_BINDABLE_PROPERTY_4(Class, Type, name, Signal) \
1233 static constexpr size_t _qt_property_##name##_offset() { \
1234 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1235 return offsetof(Class, name); \
1236 QT_WARNING_POP \
1237 } \
1238 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name;
1239
1240#define Q_OBJECT_BINDABLE_PROPERTY(...) \
1241 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1242 QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \
1243 QT_WARNING_POP
1244
1245#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_4(Class, Type, name, value) \
1246 static constexpr size_t _qt_property_##name##_offset() \
1247 { \
1248 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1249 return offsetof(Class, name); \
1250 QT_WARNING_POP \
1251 } \
1252 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name = \
1253 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr>( \
1254 value);
1255
1256#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_5(Class, Type, name, value, Signal) \
1257 static constexpr size_t _qt_property_##name##_offset() \
1258 { \
1259 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1260 return offsetof(Class, name); \
1261 QT_WARNING_POP \
1262 } \
1263 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name = \
1264 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal>( \
1265 value);
1266
1267#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...) \
1268 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1269 QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \
1270 QT_WARNING_POP
1271
1272template<typename Class, typename T, auto Offset, auto Getter>
1273class QObjectComputedProperty : public QUntypedPropertyData
1274{
1275 Class *owner()
1276 {
1277 char *that = reinterpret_cast<char *>(this);
1278 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1279 }
1280 const Class *owner() const
1281 {
1282 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
1283 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1284 }
1285
1286public:
1287 using value_type = T;
1288 using parameter_type = T;
1289
1290 QObjectComputedProperty() = default;
1291
1292 parameter_type value() const
1293 {
1294 qGetBindingStorage(owner())->registerDependency(this);
1295 return (owner()->*Getter)();
1296 }
1297
1298 std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, parameter_type, void>
1299 operator->() const
1300 {
1301 if constexpr (QTypeTraits::is_dereferenceable_v<T>)
1302 return value();
1303 else
1304 return;
1305 }
1306
1307 parameter_type operator*() const
1308 {
1309 return value();
1310 }
1311
1312 operator parameter_type() const
1313 {
1314 return value();
1315 }
1316
1317 constexpr bool hasBinding() const { return false; }
1318
1319 template<typename Functor>
1320 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1321 {
1322 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1323 return QPropertyChangeHandler<Functor>(*this, f);
1324 }
1325
1326 template<typename Functor>
1327 QPropertyChangeHandler<Functor> subscribe(Functor f)
1328 {
1329 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1330 f();
1331 return onValueChanged(f);
1332 }
1333
1334 template<typename Functor>
1335 QPropertyNotifier addNotifier(Functor f)
1336 {
1337 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1338 return QPropertyNotifier(*this, f);
1339 }
1340
1341 QtPrivate::QPropertyBindingData &bindingData() const
1342 {
1343 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1344 return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
1345 }
1346
1347 void notify() {
1348 // computed property can't store a binding, so there's nothing to mark
1349 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1350 auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
1351 if (bd)
1352 bd->notifyObservers(this, qGetBindingStorage(owner()));
1353 }
1354};
1355
1356#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
1357 static constexpr size_t _qt_property_##name##_offset() { \
1358 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1359 return offsetof(Class, name); \
1360 QT_WARNING_POP \
1361 } \
1362 QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
1363
1364#undef QT_SOURCE_LOCATION_NAMESPACE
1365
1366QT_END_NAMESPACE
1367
1368#endif // QPROPERTY_H
1369

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/corelib/kernel/qproperty.h