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(std::move(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(std::move(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(std::move(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(std::move(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
363 template <typename U, typename = void>
364 struct has_operator_equal_to : std::false_type{};
365
366 template <typename U>
367 struct has_operator_equal_to<U, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const U&>()))>>
368 : std::true_type{};
369
370 template <typename U>
371 static constexpr bool has_operator_equal_to_v =
372 !std::is_same_v<U, T> && has_operator_equal_to<U>::value;
373
374public:
375 using value_type = typename QPropertyData<T>::value_type;
376 using parameter_type = typename QPropertyData<T>::parameter_type;
377 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
378 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
379
380 QProperty() = default;
381 explicit QProperty(parameter_type initialValue) : QPropertyData<T>(initialValue) {}
382 explicit QProperty(rvalue_ref initialValue) : QPropertyData<T>(std::move(initialValue)) {}
383 explicit QProperty(const QPropertyBinding<T> &binding)
384 : QProperty()
385 { setBinding(binding); }
386#ifndef Q_QDOC
387 template <typename Functor>
388 explicit QProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
389 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
390 : QProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
391 {}
392#else
393 template <typename Functor>
394 explicit QProperty(Functor &&f);
395#endif
396 ~QProperty() = default;
397
398 QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, QProperty, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
399 QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
400 QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
401
402 QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, U, /* non-constexpr */, noexcept(false), template <typename U, std::enable_if_t<has_operator_equal_to_v<U>>* = nullptr>)
403 QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QProperty, U, /* non-constexpr */, noexcept(false), template <typename U, std::enable_if_t<has_operator_equal_to_v<U>>* = nullptr>)
404
405 // Explicitly delete op==(QProperty<T>, QProperty<U>) for different T & U.
406 // We do not want implicit conversions here!
407 // However, GCC complains about using a default template argument in a
408 // friend declaration, while Clang and MSVC are fine. So, skip GCC here.
409#if !defined(Q_CC_GNU) || defined(Q_CC_CLANG)
410#define QPROPERTY_DECL_DELETED_EQ_OP \
411 Q_DECL_EQ_DELETE_X("Call .value() on one of the properties explicitly.")
412 template <typename U, std::enable_if_t<!std::is_same_v<T, U>>* = nullptr>
413 friend void operator==(const QProperty &, const QProperty<U> &) QPROPERTY_DECL_DELETED_EQ_OP;
414 template <typename U, std::enable_if_t<!std::is_same_v<T, U>>* = nullptr>
415 friend void operator!=(const QProperty &, const QProperty<U> &) QPROPERTY_DECL_DELETED_EQ_OP;
416#undef QPROPERTY_DECL_DELETED_EQ_OP
417#endif // !defined(Q_CC_GNU) || defined(Q_CC_CLANG)
418
419 parameter_type value() const
420 {
421 d.registerWithCurrentlyEvaluatingBinding();
422 return this->val;
423 }
424
425 arrow_operator_result operator->() const
426 {
427 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
428 return value();
429 } else if constexpr (std::is_pointer_v<T>) {
430 value();
431 return this->val;
432 } else {
433 return;
434 }
435 }
436
437 parameter_type operator*() const
438 {
439 return value();
440 }
441
442 operator parameter_type() const
443 {
444 return value();
445 }
446
447 void setValue(rvalue_ref newValue)
448 {
449 d.removeBinding();
450 if (is_equal(v: newValue))
451 return;
452 this->val = std::move(newValue);
453 notify();
454 }
455
456 void setValue(parameter_type newValue)
457 {
458 d.removeBinding();
459 if (is_equal(v: newValue))
460 return;
461 this->val = newValue;
462 notify();
463 }
464
465 QProperty<T> &operator=(rvalue_ref newValue)
466 {
467 setValue(std::move(newValue));
468 return *this;
469 }
470
471 QProperty<T> &operator=(parameter_type newValue)
472 {
473 setValue(newValue);
474 return *this;
475 }
476
477 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
478 {
479 return QPropertyBinding<T>(d.setBinding(newBinding, propertyDataPtr: this));
480 }
481
482 bool setBinding(const QUntypedPropertyBinding &newBinding)
483 {
484 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
485 return false;
486 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
487 return true;
488 }
489
490#ifndef Q_QDOC
491 template <typename Functor>
492 QPropertyBinding<T> setBinding(Functor &&f,
493 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
494 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
495 {
496 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
497 }
498#else
499 template <typename Functor>
500 QPropertyBinding<T> setBinding(Functor f);
501#endif
502
503 bool hasBinding() const { return d.hasBinding(); }
504
505 QPropertyBinding<T> binding() const
506 {
507 return QPropertyBinding<T>(QUntypedPropertyBinding(d.binding()));
508 }
509
510 QPropertyBinding<T> takeBinding()
511 {
512 return QPropertyBinding<T>(d.setBinding(newBinding: QUntypedPropertyBinding(), propertyDataPtr: this));
513 }
514
515 template<typename Functor>
516 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
517 {
518 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
519 return QPropertyChangeHandler<Functor>(*this, std::move(f));
520 }
521
522 template<typename Functor>
523 QPropertyChangeHandler<Functor> subscribe(Functor f)
524 {
525 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
526 f();
527 return onValueChanged(std::move(f));
528 }
529
530 template<typename Functor>
531 QPropertyNotifier addNotifier(Functor f)
532 {
533 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
534 return QPropertyNotifier(*this, std::move(f));
535 }
536
537 const QtPrivate::QPropertyBindingData &bindingData() const { return d; }
538private:
539 template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>
540 friend bool comparesEqual(const QProperty &lhs, const QProperty &rhs)
541 {
542 return lhs.value() == rhs.value();
543 }
544
545 template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>
546 friend bool comparesEqual(const QProperty &lhs, const T &rhs)
547 {
548 return lhs.value() == rhs;
549 }
550
551 template <typename U, std::enable_if_t<has_operator_equal_to_v<U>>* = nullptr>
552 friend bool comparesEqual(const QProperty &lhs, const U &rhs)
553 {
554 return lhs.value() == rhs;
555 }
556
557 void notify()
558 {
559 d.notifyObservers(this);
560 }
561
562 Q_DISABLE_COPY_MOVE(QProperty)
563};
564
565namespace Qt {
566 template <typename PropertyType>
567 QPropertyBinding<PropertyType> makePropertyBinding(const QProperty<PropertyType> &otherProperty,
568 const QPropertyBindingSourceLocation &location =
569 QT_PROPERTY_DEFAULT_BINDING_LOCATION)
570 {
571 return Qt::makePropertyBinding([&otherProperty]() -> PropertyType { return otherProperty; }, location);
572 }
573}
574
575
576namespace QtPrivate
577{
578
579struct QBindableInterface
580{
581 using Getter = void (*)(const QUntypedPropertyData *d, void *value);
582 using Setter = void (*)(QUntypedPropertyData *d, const void *value);
583 using BindingGetter = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d);
584 using BindingSetter = QUntypedPropertyBinding (*)(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding);
585 using MakeBinding = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location);
586 using SetObserver = void (*)(const QUntypedPropertyData *d, QPropertyObserver *observer);
587 using GetMetaType = QMetaType (*)();
588 Getter getter;
589 Setter setter;
590 BindingGetter getBinding;
591 BindingSetter setBinding;
592 MakeBinding makeBinding;
593 SetObserver setObserver;
594 GetMetaType metaType;
595
596 static constexpr quintptr MetaTypeAccessorFlag = 0x1;
597};
598
599template<typename Property, typename = void>
600class QBindableInterfaceForProperty
601{
602 using T = typename Property::value_type;
603public:
604 // interface for computed properties. Those do not have a binding()/setBinding() method, but one can
605 // install observers on them.
606 static constexpr QBindableInterface iface = {
607 [](const QUntypedPropertyData *d, void *value) -> void
608 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
609 nullptr,
610 nullptr,
611 nullptr,
612 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
613 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
614 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
615 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
616 []() { return QMetaType::fromType<T>(); }
617 };
618};
619
620template<typename Property>
621class QBindableInterfaceForProperty<const Property, std::void_t<decltype(std::declval<Property>().binding())>>
622{
623 using T = typename Property::value_type;
624public:
625 // A bindable created from a const property results in a read-only interface, too.
626 static constexpr QBindableInterface iface = {
627
628 [](const QUntypedPropertyData *d, void *value) -> void
629 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
630 /*setter=*/nullptr,
631 [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
632 { return static_cast<const Property *>(d)->binding(); },
633 /*setBinding=*/nullptr,
634 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
635 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
636 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
637 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
638 []() { return QMetaType::fromType<T>(); }
639 };
640};
641
642template<typename Property>
643class QBindableInterfaceForProperty<Property, std::void_t<decltype(std::declval<Property>().binding())>>
644{
645 using T = typename Property::value_type;
646public:
647 static constexpr QBindableInterface iface = {
648 [](const QUntypedPropertyData *d, void *value) -> void
649 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
650 [](QUntypedPropertyData *d, const void *value) -> void
651 { static_cast<Property *>(d)->setValue(*static_cast<const T*>(value)); },
652 [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
653 { return static_cast<const Property *>(d)->binding(); },
654 [](QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) -> QUntypedPropertyBinding
655 { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
656 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
657 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
658 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
659 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
660 []() { return QMetaType::fromType<T>(); }
661 };
662};
663
664}
665
666namespace QtPrivate {
667// used in Q(Untyped)Bindable to print warnings about various binding errors
668namespace BindableWarnings {
669enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface };
670Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason);
671Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected);
672}
673
674namespace PropertyAdaptorSlotObjectHelpers {
675Q_CORE_EXPORT void getter(const QUntypedPropertyData *d, void *value);
676Q_CORE_EXPORT void setter(QUntypedPropertyData *d, const void *value);
677Q_CORE_EXPORT QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d);
678Q_CORE_EXPORT bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
679 QtPrivate::QPropertyBindingFunction binding,
680 QUntypedPropertyData *temp, void *value);
681Q_CORE_EXPORT QUntypedPropertyBinding setBinding(QUntypedPropertyData *d,
682 const QUntypedPropertyBinding &binding,
683 QPropertyBindingWrapper wrapper);
684Q_CORE_EXPORT void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer);
685
686template<typename T>
687bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
688 QtPrivate::QPropertyBindingFunction binding)
689{
690 struct Data : QPropertyData<T>
691 {
692 void *data() { return &this->val; }
693 } temp;
694 return bindingWrapper(type, d, binding, &temp, temp.data());
695}
696
697template<typename T>
698QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding)
699{
700 return setBinding(d, binding, &bindingWrapper<T>);
701}
702
703template<typename T>
704QUntypedPropertyBinding makeBinding(const QUntypedPropertyData *d,
705 const QPropertyBindingSourceLocation &location)
706{
707 return Qt::makePropertyBinding(
708 [d]() -> T {
709 T r;
710 getter(d, &r);
711 return r;
712 },
713 location);
714}
715
716template<class T>
717inline constexpr QBindableInterface iface = {
718 &getter,
719 &setter,
720 &getBinding,
721 &setBinding<T>,
722 &makeBinding<T>,
723 &setObserver,
724 &QMetaType::fromType<T>,
725};
726}
727}
728
729class QUntypedBindable
730{
731 friend struct QUntypedBindablePrivate; // allows access to internal data
732protected:
733 QUntypedPropertyData *data = nullptr;
734 const QtPrivate::QBindableInterface *iface = nullptr;
735 constexpr QUntypedBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
736 : data(d), iface(i)
737 {}
738
739 Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i);
740 Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i);
741
742public:
743 constexpr QUntypedBindable() = default;
744 template<typename Property>
745 QUntypedBindable(Property *p)
746 : data(const_cast<std::remove_cv_t<Property> *>(p)),
747 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
748 { Q_ASSERT(data && iface); }
749
750 bool isValid() const { return data != nullptr; }
751 bool isBindable() const { return iface && iface->getBinding; }
752 bool isReadOnly() const { return !(iface && iface->setBinding && iface->setObserver); }
753
754 QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
755 {
756 return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
757 }
758
759 QUntypedPropertyBinding takeBinding()
760 {
761 if (!iface)
762 return QUntypedPropertyBinding {};
763 // We do not have a dedicated takeBinding function pointer in the interface
764 // therefore we synthesize takeBinding by retrieving the binding with binding
765 // and calling setBinding with a default constructed QUntypedPropertyBinding
766 // afterwards.
767 if (!(iface->getBinding && iface->setBinding))
768 return QUntypedPropertyBinding {};
769 QUntypedPropertyBinding binding = iface->getBinding(data);
770 iface->setBinding(data, QUntypedPropertyBinding{});
771 return binding;
772 }
773
774 void observe(QPropertyObserver *observer) const
775 {
776 if (iface)
777 iface->setObserver(data, observer);
778#ifndef QT_NO_DEBUG
779 else
780 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "observe:",
781 reason: QtPrivate::BindableWarnings::InvalidInterface);
782#endif
783 }
784
785 template<typename Functor>
786 QPropertyChangeHandler<Functor> onValueChanged(Functor f) const
787 {
788 QPropertyChangeHandler<Functor> handler(std::move(f));
789 observe(observer: &handler);
790 return handler;
791 }
792
793 template<typename Functor>
794 QPropertyChangeHandler<Functor> subscribe(Functor f) const
795 {
796 f();
797 return onValueChanged(std::move(f));
798 }
799
800 template<typename Functor>
801 QPropertyNotifier addNotifier(Functor f)
802 {
803 QPropertyNotifier handler(std::move(f));
804 observe(observer: &handler);
805 return handler;
806 }
807
808 QUntypedPropertyBinding binding() const
809 {
810 if (!isBindable()) {
811#ifndef QT_NO_DEBUG
812 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "binding: ",
813 reason: QtPrivate::BindableWarnings::NonBindableInterface);
814#endif
815 return QUntypedPropertyBinding();
816 }
817 return iface->getBinding(data);
818 }
819 bool setBinding(const QUntypedPropertyBinding &binding)
820 {
821 if (isReadOnly()) {
822#ifndef QT_NO_DEBUG
823 const auto errorType = iface ? QtPrivate::BindableWarnings::ReadOnlyInterface :
824 QtPrivate::BindableWarnings::InvalidInterface;
825 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding: Could not set binding via bindable interface.", reason: errorType);
826#endif
827 return false;
828 }
829 if (!binding.isNull() && binding.valueMetaType() != metaType()) {
830#ifndef QT_NO_DEBUG
831 QtPrivate::BindableWarnings::printMetaTypeMismatch(actual: metaType(), expected: binding.valueMetaType());
832#endif
833 return false;
834 }
835 iface->setBinding(data, binding);
836 return true;
837 }
838 bool hasBinding() const
839 {
840 return !binding().isNull();
841 }
842
843 QMetaType metaType() const
844 {
845 if (!(iface && data))
846 return QMetaType();
847 if (iface->metaType)
848 return iface->metaType();
849 // ### Qt 7: Change the metatype function to take data as its argument
850 // special casing for QML's proxy bindable: allow multiplexing in the getter
851 // function to retrieve the metatype from data
852 Q_ASSERT(iface->getter);
853 QMetaType result;
854 iface->getter(data, reinterpret_cast<void *>(quintptr(&result) | QtPrivate::QBindableInterface::MetaTypeAccessorFlag));
855 return result;
856 }
857
858};
859
860template<typename T>
861class QBindable : public QUntypedBindable
862{
863 template<typename U>
864 friend class QPropertyAlias;
865 constexpr QBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
866 : QUntypedBindable(d, i)
867 {}
868public:
869 using QUntypedBindable::QUntypedBindable;
870 explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b)
871 {
872 if (iface && metaType() != QMetaType::fromType<T>()) {
873 data = nullptr;
874 iface = nullptr;
875 }
876 }
877
878 explicit QBindable(QObject *obj, const QMetaProperty &property)
879 : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
880
881 explicit QBindable(QObject *obj, const char *property)
882 : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
883
884 QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
885 {
886 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
887 }
888 QPropertyBinding<T> binding() const
889 {
890 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding());
891 }
892
893 QPropertyBinding<T> takeBinding()
894 {
895 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::takeBinding());
896 }
897
898 using QUntypedBindable::setBinding;
899 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
900 {
901 Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == metaType());
902
903 if (iface && iface->setBinding)
904 return static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding));
905#ifndef QT_NO_DEBUG
906 if (!iface)
907 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding", reason: QtPrivate::BindableWarnings::InvalidInterface);
908 else
909 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding: Could not set binding via bindable interface.", reason: QtPrivate::BindableWarnings::ReadOnlyInterface);
910#endif
911 return QPropertyBinding<T>();
912 }
913#ifndef Q_QDOC
914 template <typename Functor>
915 QPropertyBinding<T> setBinding(Functor &&f,
916 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
917 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
918 {
919 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
920 }
921#else
922 template <typename Functor>
923 QPropertyBinding<T> setBinding(Functor f);
924#endif
925
926 T value() const
927 {
928 if (iface) {
929 T result;
930 iface->getter(data, &result);
931 return result;
932 }
933 return T{};
934 }
935
936 void setValue(const T &value)
937 {
938 if (iface && iface->setter)
939 iface->setter(data, &value);
940 }
941};
942
943#if QT_DEPRECATED_SINCE(6, 6)
944template<typename T>
945class QT_DEPRECATED_VERSION_X_6_6("Class was only meant for internal use, use a QProperty and add a binding to the target")
946QPropertyAlias : public QPropertyObserver
947{
948 Q_DISABLE_COPY_MOVE(QPropertyAlias)
949 const QtPrivate::QBindableInterface *iface = nullptr;
950
951public:
952 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
953 QPropertyAlias(QProperty<T> *property)
954 : QPropertyObserver(property),
955 iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface)
956 {
957 if (iface)
958 iface->setObserver(aliasedProperty(), this);
959 }
960
961 template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
962 QPropertyAlias(Property *property)
963 : QPropertyObserver(property),
964 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
965 {
966 if (iface)
967 iface->setObserver(aliasedProperty(), this);
968 }
969
970 QPropertyAlias(QPropertyAlias<T> *alias)
971 : QPropertyObserver(alias->aliasedProperty()),
972 iface(alias->iface)
973 {
974 if (iface)
975 iface->setObserver(aliasedProperty(), this);
976 }
977
978 QPropertyAlias(const QBindable<T> &property)
979 : QPropertyObserver(property.data),
980 iface(property.iface)
981 {
982 if (iface)
983 iface->setObserver(aliasedProperty(), this);
984 }
985
986 T value() const
987 {
988 T t = T();
989 if (auto *p = aliasedProperty())
990 iface->getter(p, &t);
991 return t;
992 }
993
994 operator T() const { return value(); }
995
996 void setValue(const T &newValue)
997 {
998 if (auto *p = aliasedProperty())
999 iface->setter(p, &newValue);
1000 }
1001
1002 QPropertyAlias<T> &operator=(const T &newValue)
1003 {
1004 setValue(newValue);
1005 return *this;
1006 }
1007
1008 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
1009 {
1010 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
1011 }
1012
1013 bool setBinding(const QUntypedPropertyBinding &newBinding)
1014 {
1015 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
1016 }
1017
1018#ifndef Q_QDOC
1019 template <typename Functor>
1020 QPropertyBinding<T> setBinding(Functor &&f,
1021 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
1022 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
1023 {
1024 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
1025 }
1026#else
1027 template <typename Functor>
1028 QPropertyBinding<T> setBinding(Functor f);
1029#endif
1030
1031 bool hasBinding() const
1032 {
1033 return QBindable<T>(aliasedProperty(), iface).hasBinding();
1034 }
1035
1036 QPropertyBinding<T> binding() const
1037 {
1038 return QBindable<T>(aliasedProperty(), iface).binding();
1039 }
1040
1041 QPropertyBinding<T> takeBinding()
1042 {
1043 return QBindable<T>(aliasedProperty(), iface).takeBinding();
1044 }
1045
1046 template<typename Functor>
1047 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1048 {
1049 return QBindable<T>(aliasedProperty(), iface).onValueChanged(std::move(f));
1050 }
1051
1052 template<typename Functor>
1053 QPropertyChangeHandler<Functor> subscribe(Functor f)
1054 {
1055 return QBindable<T>(aliasedProperty(), iface).subscribe(std::move(f));
1056 }
1057
1058 template<typename Functor>
1059 QPropertyNotifier addNotifier(Functor f)
1060 {
1061 return QBindable<T>(aliasedProperty(), iface).addNotifier(std::move(f));
1062 }
1063
1064 bool isValid() const
1065 {
1066 return aliasedProperty() != nullptr;
1067 }
1068 QT_WARNING_POP
1069};
1070#endif // QT_DEPRECATED_SINCE(6, 6)
1071
1072template<typename Class, typename T, auto Offset, auto Signal = nullptr>
1073class QObjectBindableProperty : public QPropertyData<T>
1074{
1075 using ThisType = QObjectBindableProperty<Class, T, Offset, Signal>;
1076 static bool constexpr HasSignal = !std::is_same_v<decltype(Signal), std::nullptr_t>;
1077 using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
1078 Class *owner()
1079 {
1080 char *that = reinterpret_cast<char *>(this);
1081 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1082 }
1083 const Class *owner() const
1084 {
1085 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
1086 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1087 }
1088 static void signalCallBack(QUntypedPropertyData *o)
1089 {
1090 QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o);
1091 if constexpr (HasSignal) {
1092 if constexpr (SignalTakesValue::value)
1093 (that->owner()->*Signal)(that->valueBypassingBindings());
1094 else
1095 (that->owner()->*Signal)();
1096 }
1097 }
1098public:
1099 using value_type = typename QPropertyData<T>::value_type;
1100 using parameter_type = typename QPropertyData<T>::parameter_type;
1101 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
1102 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
1103
1104 QObjectBindableProperty() = default;
1105 explicit QObjectBindableProperty(const T &initialValue) : QPropertyData<T>(initialValue) {}
1106 explicit QObjectBindableProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue)) {}
1107 explicit QObjectBindableProperty(const QPropertyBinding<T> &binding)
1108 : QObjectBindableProperty()
1109 { setBinding(binding); }
1110#ifndef Q_QDOC
1111 template <typename Functor>
1112 explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
1113 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
1114 : QObjectBindableProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
1115 {}
1116#else
1117 template <typename Functor>
1118 explicit QObjectBindableProperty(Functor &&f);
1119#endif
1120
1121 QT_DECLARE_EQUALITY_OPERATORS_HELPER(QObjectBindableProperty, QObjectBindableProperty, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
1122 QT_DECLARE_EQUALITY_OPERATORS_HELPER(QObjectBindableProperty, T, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
1123 QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QObjectBindableProperty, T, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
1124
1125 parameter_type value() const
1126 {
1127 qGetBindingStorage(owner())->registerDependency(this);
1128 return this->val;
1129 }
1130
1131 arrow_operator_result operator->() const
1132 {
1133 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
1134 return value();
1135 } else if constexpr (std::is_pointer_v<T>) {
1136 value();
1137 return this->val;
1138 } else {
1139 return;
1140 }
1141 }
1142
1143 parameter_type operator*() const
1144 {
1145 return value();
1146 }
1147
1148 operator parameter_type() const
1149 {
1150 return value();
1151 }
1152
1153 void setValue(parameter_type t)
1154 {
1155 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1156 if (bd)
1157 bd->removeBinding();
1158 if (this->val == t)
1159 return;
1160 this->val = t;
1161 notify(bd);
1162 }
1163
1164 void notify() {
1165 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1166 notify(bd);
1167 }
1168
1169 void setValue(rvalue_ref t)
1170 {
1171 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1172 if (bd)
1173 bd->removeBinding();
1174 if (this->val == t)
1175 return;
1176 this->val = std::move(t);
1177 notify(bd);
1178 }
1179
1180 QObjectBindableProperty &operator=(rvalue_ref newValue)
1181 {
1182 setValue(std::move(newValue));
1183 return *this;
1184 }
1185
1186 QObjectBindableProperty &operator=(parameter_type newValue)
1187 {
1188 setValue(newValue);
1189 return *this;
1190 }
1191
1192 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
1193 {
1194 QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
1195 QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, propertyDataPtr: this, staticObserverCallback: HasSignal ? &signalCallBack : nullptr));
1196 return static_cast<QPropertyBinding<T> &>(oldBinding);
1197 }
1198
1199 bool setBinding(const QUntypedPropertyBinding &newBinding)
1200 {
1201 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
1202 return false;
1203 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
1204 return true;
1205 }
1206
1207#ifndef Q_QDOC
1208 template <typename Functor>
1209 QPropertyBinding<T> setBinding(Functor &&f,
1210 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
1211 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
1212 {
1213 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
1214 }
1215#else
1216 template <typename Functor>
1217 QPropertyBinding<T> setBinding(Functor f);
1218#endif
1219
1220 bool hasBinding() const
1221 {
1222 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1223 return bd && bd->binding() != nullptr;
1224 }
1225
1226 QPropertyBinding<T> binding() const
1227 {
1228 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1229 return static_cast<QPropertyBinding<T> &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr));
1230 }
1231
1232 QPropertyBinding<T> takeBinding()
1233 {
1234 return setBinding(QPropertyBinding<T>());
1235 }
1236
1237 template<typename Functor>
1238 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1239 {
1240 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1241 return QPropertyChangeHandler<Functor>(*this, std::move(f));
1242 }
1243
1244 template<typename Functor>
1245 QPropertyChangeHandler<Functor> subscribe(Functor f)
1246 {
1247 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1248 f();
1249 return onValueChanged(std::move(f));
1250 }
1251
1252 template<typename Functor>
1253 QPropertyNotifier addNotifier(Functor f)
1254 {
1255 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1256 return QPropertyNotifier(*this, std::move(f));
1257 }
1258
1259 const QtPrivate::QPropertyBindingData &bindingData() const
1260 {
1261 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1262 return *storage->bindingData(const_cast<ThisType *>(this), true);
1263 }
1264private:
1265 template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>
1266 friend bool comparesEqual(const QObjectBindableProperty &lhs, const QObjectBindableProperty &rhs)
1267 {
1268 return lhs.value() == rhs.value();
1269 }
1270
1271 template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>
1272 friend bool comparesEqual(const QObjectBindableProperty &lhs, const T &rhs)
1273 {
1274 return lhs.value() == rhs;
1275 }
1276
1277 void notify(const QtPrivate::QPropertyBindingData *binding)
1278 {
1279 if (binding)
1280 binding->notifyObservers(this, qGetBindingStorage(owner()));
1281 if constexpr (HasSignal) {
1282 if constexpr (SignalTakesValue::value)
1283 (owner()->*Signal)(this->valueBypassingBindings());
1284 else
1285 (owner()->*Signal)();
1286 }
1287 }
1288};
1289
1290#define QT_OBJECT_BINDABLE_PROPERTY_3(Class, Type, name) \
1291 static constexpr size_t _qt_property_##name##_offset() { \
1292 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1293 return offsetof(Class, name); \
1294 QT_WARNING_POP \
1295 } \
1296 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name;
1297
1298#define QT_OBJECT_BINDABLE_PROPERTY_4(Class, Type, name, Signal) \
1299 static constexpr size_t _qt_property_##name##_offset() { \
1300 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1301 return offsetof(Class, name); \
1302 QT_WARNING_POP \
1303 } \
1304 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name;
1305
1306#define Q_OBJECT_BINDABLE_PROPERTY(...) \
1307 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1308 QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \
1309 QT_WARNING_POP
1310
1311#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_4(Class, Type, name, value) \
1312 static constexpr size_t _qt_property_##name##_offset() \
1313 { \
1314 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1315 return offsetof(Class, name); \
1316 QT_WARNING_POP \
1317 } \
1318 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name = \
1319 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr>( \
1320 value);
1321
1322#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_5(Class, Type, name, value, Signal) \
1323 static constexpr size_t _qt_property_##name##_offset() \
1324 { \
1325 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1326 return offsetof(Class, name); \
1327 QT_WARNING_POP \
1328 } \
1329 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name = \
1330 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal>( \
1331 value);
1332
1333#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...) \
1334 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1335 QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \
1336 QT_WARNING_POP
1337
1338template<typename Class, typename T, auto Offset, auto Getter>
1339class QObjectComputedProperty : public QUntypedPropertyData
1340{
1341 Class *owner()
1342 {
1343 char *that = reinterpret_cast<char *>(this);
1344 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1345 }
1346 const Class *owner() const
1347 {
1348 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
1349 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1350 }
1351
1352public:
1353 using value_type = T;
1354 using parameter_type = T;
1355
1356 QObjectComputedProperty() = default;
1357
1358 parameter_type value() const
1359 {
1360 qGetBindingStorage(owner())->registerDependency(this);
1361 return (owner()->*Getter)();
1362 }
1363
1364 std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, parameter_type, void>
1365 operator->() const
1366 {
1367 if constexpr (QTypeTraits::is_dereferenceable_v<T>)
1368 return value();
1369 else
1370 return;
1371 }
1372
1373 parameter_type operator*() const
1374 {
1375 return value();
1376 }
1377
1378 operator parameter_type() const
1379 {
1380 return value();
1381 }
1382
1383 constexpr bool hasBinding() const { return false; }
1384
1385 template<typename Functor>
1386 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1387 {
1388 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1389 return QPropertyChangeHandler<Functor>(*this, std::move(f));
1390 }
1391
1392 template<typename Functor>
1393 QPropertyChangeHandler<Functor> subscribe(Functor f)
1394 {
1395 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1396 f();
1397 return onValueChanged(std::move(f));
1398 }
1399
1400 template<typename Functor>
1401 QPropertyNotifier addNotifier(Functor f)
1402 {
1403 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1404 return QPropertyNotifier(*this, std::move(f));
1405 }
1406
1407 QtPrivate::QPropertyBindingData &bindingData() const
1408 {
1409 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1410 return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
1411 }
1412
1413 void notify() {
1414 // computed property can't store a binding, so there's nothing to mark
1415 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1416 auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
1417 if (bd)
1418 bd->notifyObservers(this, qGetBindingStorage(owner()));
1419 }
1420};
1421
1422#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
1423 static constexpr size_t _qt_property_##name##_offset() { \
1424 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1425 return offsetof(Class, name); \
1426 QT_WARNING_POP \
1427 } \
1428 QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
1429
1430#undef QT_SOURCE_LOCATION_NAMESPACE
1431
1432QT_END_NAMESPACE
1433
1434#endif // QPROPERTY_H
1435

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