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