1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QPROPERTY_H
41#define QPROPERTY_H
42
43#include <QtCore/qglobal.h>
44#include <QtCore/QSharedDataPointer>
45#include <QtCore/QString>
46#include <functional>
47#include <type_traits>
48#include <variant>
49
50#include <QtCore/qpropertyprivate.h>
51
52#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_CLANG_QDOC)
53#include <experimental/source_location>
54#define QT_PROPERTY_COLLECT_BINDING_LOCATION
55#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current())
56#elif __has_include(<experimental/source_location>) && __cplusplus >= 201703L && !defined(Q_CLANG_QDOC)
57#include <experimental/source_location>
58#define QT_PROPERTY_COLLECT_BINDING_LOCATION
59#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::experimental::source_location::current())
60#else
61#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation()
62#endif
63
64QT_BEGIN_NAMESPACE
65
66template <typename T>
67class QPropertyData : public QUntypedPropertyData
68{
69protected:
70 mutable T val = T();
71private:
72 class DisableRValueRefs {};
73protected:
74 static constexpr bool UseReferences = !(std::is_arithmetic_v<T> || std::is_enum_v<T> || std::is_pointer_v<T>);
75public:
76 using value_type = T;
77 using parameter_type = std::conditional_t<UseReferences, const T &, T>;
78 using rvalue_ref = typename std::conditional_t<UseReferences, T &&, DisableRValueRefs>;
79 using arrow_operator_result = std::conditional_t<std::is_pointer_v<T>, const T &,
80 std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, const T &, void>>;
81
82 QPropertyData() = default;
83 QPropertyData(parameter_type t) : val(t) {}
84 QPropertyData(rvalue_ref t) : val(std::move(t)) {}
85 ~QPropertyData() = default;
86
87 parameter_type valueBypassingBindings() const { return val; }
88 void setValueBypassingBindings(parameter_type v) { val = v; }
89 void setValueBypassingBindings(rvalue_ref v) { val = std::move(v); }
90};
91
92struct Q_CORE_EXPORT QPropertyBindingSourceLocation
93{
94 const char *fileName = nullptr;
95 const char *functionName = nullptr;
96 quint32 line = 0;
97 quint32 column = 0;
98 QPropertyBindingSourceLocation() = default;
99#ifdef QT_PROPERTY_COLLECT_BINDING_LOCATION
100 QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation)
101 {
102 fileName = cppLocation.file_name();
103 functionName = cppLocation.function_name();
104 line = cppLocation.line();
105 column = cppLocation.column();
106 }
107#endif
108};
109
110template <typename Functor> class QPropertyChangeHandler;
111class QPropertyBindingErrorPrivate;
112
113class Q_CORE_EXPORT QPropertyBindingError
114{
115public:
116 enum Type {
117 NoError,
118 BindingLoop,
119 EvaluationError,
120 UnknownError
121 };
122
123 QPropertyBindingError();
124 QPropertyBindingError(Type type, const QString &description = QString());
125
126 QPropertyBindingError(const QPropertyBindingError &other);
127 QPropertyBindingError &operator=(const QPropertyBindingError &other);
128 QPropertyBindingError(QPropertyBindingError &&other);
129 QPropertyBindingError &operator=(QPropertyBindingError &&other);
130 ~QPropertyBindingError();
131
132 bool hasError() const { return d.get() != nullptr; }
133 Type type() const;
134 QString description() const;
135
136private:
137 QSharedDataPointer<QPropertyBindingErrorPrivate> d;
138};
139
140class Q_CORE_EXPORT QUntypedPropertyBinding
141{
142public:
143 // writes binding result into dataPtr
144 using BindingEvaluationFunction = QtPrivate::QPropertyBindingFunction;
145
146 QUntypedPropertyBinding();
147 QUntypedPropertyBinding(QMetaType metaType, BindingEvaluationFunction function, const QPropertyBindingSourceLocation &location);
148 QUntypedPropertyBinding(QUntypedPropertyBinding &&other);
149 QUntypedPropertyBinding(const QUntypedPropertyBinding &other);
150 QUntypedPropertyBinding &operator=(const QUntypedPropertyBinding &other);
151 QUntypedPropertyBinding &operator=(QUntypedPropertyBinding &&other);
152 ~QUntypedPropertyBinding();
153
154 bool isNull() const;
155
156 QPropertyBindingError error() const;
157
158 QMetaType valueMetaType() const;
159
160 explicit QUntypedPropertyBinding(QPropertyBindingPrivate *priv);
161private:
162 friend class QtPrivate::QPropertyBindingData;
163 friend class QPropertyBindingPrivate;
164 template <typename> friend class QPropertyBinding;
165 QPropertyBindingPrivatePtr d;
166};
167
168template <typename PropertyType>
169class QPropertyBinding : public QUntypedPropertyBinding
170{
171 template <typename Functor>
172 struct BindingAdaptor
173 {
174 Functor impl;
175 bool operator()(QMetaType /*metaType*/, QUntypedPropertyData *dataPtr)
176 {
177 QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr);
178 PropertyType newValue = impl();
179 if constexpr (QTypeTraits::has_operator_equal_v<PropertyType>) {
180 if (newValue == propertyPtr->valueBypassingBindings())
181 return false;
182 }
183 propertyPtr->setValueBypassingBindings(std::move(newValue));
184 return true;
185 }
186 };
187
188public:
189 QPropertyBinding() = default;
190
191 template<typename Functor>
192 QPropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location)
193 : QUntypedPropertyBinding(QMetaType::fromType<PropertyType>(), BindingAdaptor<Functor>{std::forward<Functor>(f)}, location)
194 {}
195
196 template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
197 QPropertyBinding(const Property &property)
198 : QUntypedPropertyBinding(property.bindingData().binding())
199 {}
200
201 // Internal
202 explicit QPropertyBinding(const QUntypedPropertyBinding &binding)
203 : QUntypedPropertyBinding(binding)
204 {}
205};
206
207namespace Qt {
208 template <typename Functor>
209 auto makePropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
210 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
211 {
212 return QPropertyBinding<std::invoke_result_t<Functor>>(std::forward<Functor>(f), location);
213 }
214}
215
216struct QPropertyObserverPrivate;
217struct QPropertyObserverPointer;
218
219class Q_CORE_EXPORT QPropertyObserver
220{
221public:
222 // Internal
223 enum ObserverTag {
224 ObserverNotifiesBinding, // observer was installed to notify bindings that obsverved property changed
225 ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change
226 ObserverNotifiesAlias, // used for QPropertyAlias
227 ActivelyExecuting // the observer is currently evaluated in QPropertyObserver::notifyObservers or its
228 // placeholder. We only can store 4 different values, therefore those two conflate
229 };
230
231 constexpr QPropertyObserver() = default;
232 QPropertyObserver(QPropertyObserver &&other) noexcept;
233 QPropertyObserver &operator=(QPropertyObserver &&other) noexcept;
234 ~QPropertyObserver();
235
236 template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
237 void setSource(const Property &property)
238 { setSource(property.bindingData()); }
239 void setSource(const QtPrivate::QPropertyBindingData &property);
240
241protected:
242 using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
243 QPropertyObserver(ChangeHandler changeHandler);
244 QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr);
245
246 QUntypedPropertyData *aliasedProperty() const
247 {
248 return aliasedPropertyData;
249 }
250
251private:
252
253 QTaggedPointer<QPropertyObserver, ObserverTag> next;
254 // prev is a pointer to the "next" element within the previous node, or to the "firstObserverPtr" if it is the
255 // first node.
256 QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
257
258 union {
259 QPropertyBindingPrivate *bindingToMarkDirty = nullptr;
260 ChangeHandler changeHandler;
261 QUntypedPropertyData *aliasedPropertyData;
262 QPropertyObserver **nodeState;
263 };
264
265 QPropertyObserver(const QPropertyObserver &) = delete;
266 QPropertyObserver &operator=(const QPropertyObserver &) = delete;
267
268 friend struct QPropertyObserverPointer;
269 friend struct QPropertyBindingDataPointer;
270 friend class QPropertyBindingPrivate;
271 template<ObserverTag>
272 friend struct QPropertyObserverNodeProtector;
273};
274
275template <typename Functor>
276class [[nodiscard]] QPropertyChangeHandler : public QPropertyObserver
277{
278 Functor m_handler;
279public:
280 QPropertyChangeHandler(Functor handler)
281 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
282 auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
283 This->m_handler();
284 })
285 , m_handler(handler)
286 {
287 }
288
289 template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
290 QPropertyChangeHandler(const Property &property, Functor handler)
291 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
292 auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
293 This->m_handler();
294 })
295 , m_handler(handler)
296 {
297 setSource(property);
298 }
299};
300
301template <typename T>
302class QProperty : public QPropertyData<T>
303{
304 QtPrivate::QPropertyBindingData d;
305 bool is_equal(const T &v)
306 {
307 if constexpr (QTypeTraits::has_operator_equal_v<T>) {
308 if (v == this->val)
309 return true;
310 }
311 return false;
312 }
313
314public:
315 using value_type = typename QPropertyData<T>::value_type;
316 using parameter_type = typename QPropertyData<T>::parameter_type;
317 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
318 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
319
320 QProperty() = default;
321 explicit QProperty(parameter_type initialValue) : QPropertyData<T>(initialValue) {}
322 explicit QProperty(rvalue_ref initialValue) : QPropertyData<T>(std::move(initialValue)) {}
323 explicit QProperty(const QPropertyBinding<T> &binding)
324 : QProperty()
325 { setBinding(binding); }
326#ifndef Q_CLANG_QDOC
327 template <typename Functor>
328 explicit QProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
329 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
330 : QProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
331 {}
332#else
333 template <typename Functor>
334 explicit QProperty(Functor &&f);
335#endif
336 ~QProperty() = default;
337
338 parameter_type value() const
339 {
340 if (d.hasBinding())
341 d.evaluateIfDirty(this);
342 d.registerWithCurrentlyEvaluatingBinding();
343 return this->val;
344 }
345
346 arrow_operator_result operator->() const
347 {
348 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
349 return value();
350 } else if constexpr (std::is_pointer_v<T>) {
351 value();
352 return this->val;
353 } else {
354 return;
355 }
356 }
357
358 parameter_type operator*() const
359 {
360 return value();
361 }
362
363 operator parameter_type() const
364 {
365 return value();
366 }
367
368 void setValue(rvalue_ref newValue)
369 {
370 d.removeBinding();
371 if (is_equal(newValue))
372 return;
373 this->val = std::move(newValue);
374 notify();
375 }
376
377 void setValue(parameter_type newValue)
378 {
379 d.removeBinding();
380 if (is_equal(newValue))
381 return;
382 this->val = newValue;
383 notify();
384 }
385
386 QProperty<T> &operator=(rvalue_ref newValue)
387 {
388 setValue(std::move(newValue));
389 return *this;
390 }
391
392 QProperty<T> &operator=(parameter_type newValue)
393 {
394 setValue(newValue);
395 return *this;
396 }
397
398 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
399 {
400 QPropertyBinding<T> oldBinding(d.setBinding(newBinding, this));
401 notify();
402 return oldBinding;
403 }
404
405 bool setBinding(const QUntypedPropertyBinding &newBinding)
406 {
407 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
408 return false;
409 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
410 return true;
411 }
412
413#ifndef Q_CLANG_QDOC
414 template <typename Functor>
415 QPropertyBinding<T> setBinding(Functor &&f,
416 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
417 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
418 {
419 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
420 }
421#else
422 template <typename Functor>
423 QPropertyBinding<T> setBinding(Functor f);
424#endif
425
426 bool hasBinding() const { return d.hasBinding(); }
427
428 QPropertyBinding<T> binding() const
429 {
430 return QPropertyBinding<T>(*this);
431 }
432
433 QPropertyBinding<T> takeBinding()
434 {
435 return QPropertyBinding<T>(d.setBinding(QUntypedPropertyBinding(), this));
436 }
437
438 template<typename Functor>
439 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
440 {
441 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
442 return QPropertyChangeHandler<Functor>(*this, f);
443 }
444
445 template<typename Functor>
446 QPropertyChangeHandler<Functor> subscribe(Functor f)
447 {
448 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
449 f();
450 return onValueChanged(f);
451 }
452
453 const QtPrivate::QPropertyBindingData &bindingData() const { return d; }
454private:
455 void notify()
456 {
457 d.notifyObservers(this);
458 }
459
460 Q_DISABLE_COPY_MOVE(QProperty)
461};
462
463namespace Qt {
464 template <typename PropertyType>
465 QPropertyBinding<PropertyType> makePropertyBinding(const QProperty<PropertyType> &otherProperty,
466 const QPropertyBindingSourceLocation &location =
467 QT_PROPERTY_DEFAULT_BINDING_LOCATION)
468 {
469 return Qt::makePropertyBinding([&otherProperty]() -> PropertyType { return otherProperty; }, location);
470 }
471}
472
473
474namespace QtPrivate
475{
476
477struct QBindableInterface
478{
479 using Getter = void (*)(const QUntypedPropertyData *d, void *value);
480 using Setter = void (*)(QUntypedPropertyData *d, const void *value);
481 using BindingGetter = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d);
482 using BindingSetter = QUntypedPropertyBinding (*)(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding);
483 using MakeBinding = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location);
484 using SetObserver = void (*)(const QUntypedPropertyData *d, QPropertyObserver *observer);
485 using GetMetaType = QMetaType (*)();
486 Getter getter;
487 Setter setter;
488 BindingGetter getBinding;
489 BindingSetter setBinding;
490 MakeBinding makeBinding;
491 SetObserver setObserver;
492 GetMetaType metaType;
493};
494
495template<typename Property, typename = void>
496class QBindableInterfaceForProperty
497{
498 using T = typename Property::value_type;
499public:
500 // interface for read-only properties. Those do not have a binding()/setBinding() method, but one can
501 // install observers on them.
502 static constexpr QBindableInterface iface = {
503 [](const QUntypedPropertyData *d, void *value) -> void
504 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
505 nullptr,
506 nullptr,
507 nullptr,
508 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
509 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
510 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
511 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
512 []() { return QMetaType::fromType<T>(); }
513 };
514};
515
516template<typename Property>
517class QBindableInterfaceForProperty<Property, std::void_t<decltype(std::declval<Property>().binding())>>
518{
519 using T = typename Property::value_type;
520public:
521 static constexpr QBindableInterface iface = {
522 [](const QUntypedPropertyData *d, void *value) -> void
523 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
524 [](QUntypedPropertyData *d, const void *value) -> void
525 { static_cast<Property *>(d)->setValue(*static_cast<const T*>(value)); },
526 [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
527 { return static_cast<const Property *>(d)->binding(); },
528 [](QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) -> QUntypedPropertyBinding
529 { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
530 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
531 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
532 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
533 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
534 []() { return QMetaType::fromType<T>(); }
535 };
536};
537
538}
539
540class QUntypedBindable
541{
542protected:
543 QUntypedPropertyData *data = nullptr;
544 const QtPrivate::QBindableInterface *iface = nullptr;
545 constexpr QUntypedBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
546 : data(d), iface(i)
547 {}
548
549public:
550 constexpr QUntypedBindable() = default;
551 template<typename Property>
552 QUntypedBindable(Property *p)
553 : data(const_cast<std::remove_cv_t<Property> *>(p)),
554 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
555 { Q_ASSERT(data && iface); }
556
557 bool isValid() const { return data != nullptr; }
558 bool isBindable() const { return iface && iface->getBinding; }
559
560 QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION)
561 {
562 return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
563 }
564 void observe(QPropertyObserver *observer)
565 {
566 if (iface)
567 iface->setObserver(data, observer);
568 }
569
570 template<typename Functor>
571 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
572 {
573 QPropertyChangeHandler<Functor> handler(f);
574 observe(&handler);
575 return handler;
576 }
577
578 template<typename Functor>
579 QPropertyChangeHandler<Functor> subscribe(Functor f)
580 {
581 f();
582 return onValueChanged(f);
583 }
584
585 QUntypedPropertyBinding binding() const
586 {
587 if (!iface->getBinding)
588 return QUntypedPropertyBinding();
589 return iface->getBinding(data);
590 }
591 bool setBinding(const QUntypedPropertyBinding &binding)
592 {
593 if (!iface->setBinding)
594 return false;
595 if (!binding.isNull() && binding.valueMetaType() != iface->metaType())
596 return false;
597 iface->setBinding(data, binding);
598 return true;
599 }
600 bool hasBinding() const
601 {
602 return !binding().isNull();
603 }
604
605};
606
607template<typename T>
608class QBindable : public QUntypedBindable
609{
610 template<typename U>
611 friend class QPropertyAlias;
612 constexpr QBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
613 : QUntypedBindable(d, i)
614 {}
615public:
616 using QUntypedBindable::QUntypedBindable;
617 explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b)
618 {
619 if (iface && iface->metaType() != QMetaType::fromType<T>()) {
620 data = nullptr;
621 iface = nullptr;
622 }
623 }
624
625 QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION)
626 {
627 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
628 }
629 QPropertyBinding<T> binding() const
630 {
631 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding());
632 }
633 using QUntypedBindable::setBinding;
634 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
635 {
636 Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == iface->metaType());
637 return iface ? static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding)) : QPropertyBinding<T>();
638 }
639#ifndef Q_CLANG_QDOC
640 template <typename Functor>
641 QPropertyBinding<T> setBinding(Functor &&f,
642 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
643 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
644 {
645 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
646 }
647#else
648 template <typename Functor>
649 QPropertyBinding<T> setBinding(Functor f);
650#endif
651
652 T value() const
653 {
654 T result;
655 if (iface)
656 iface->getter(data, &result);
657 return result;
658 }
659
660 void setValue(const T &value)
661 {
662 if (iface)
663 iface->setter(data, &value);
664 }
665};
666
667template<typename T>
668class QPropertyAlias : public QPropertyObserver
669{
670 Q_DISABLE_COPY_MOVE(QPropertyAlias)
671 const QtPrivate::QBindableInterface *iface = nullptr;
672
673public:
674 QPropertyAlias(QProperty<T> *property)
675 : QPropertyObserver(property),
676 iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface)
677 {
678 if (iface)
679 iface->setObserver(aliasedProperty(), this);
680 }
681
682 template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
683 QPropertyAlias(Property *property)
684 : QPropertyObserver(property),
685 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
686 {
687 if (iface)
688 iface->setObserver(aliasedProperty(), this);
689 }
690
691 QPropertyAlias(QPropertyAlias<T> *alias)
692 : QPropertyObserver(alias->aliasedProperty()),
693 iface(alias->iface)
694 {
695 if (iface)
696 iface->setObserver(aliasedProperty(), this);
697 }
698
699 QPropertyAlias(const QBindable<T> &property)
700 : QPropertyObserver(property.data),
701 iface(property.iface)
702 {
703 if (iface)
704 iface->setObserver(aliasedProperty(), this);
705 }
706
707 T value() const
708 {
709 T t = T();
710 if (auto *p = aliasedProperty())
711 iface->getter(p, &t);
712 return t;
713 }
714
715 operator T() const { return value(); }
716
717 void setValue(const T &newValue)
718 {
719 if (auto *p = aliasedProperty())
720 iface->setter(p, &newValue);
721 }
722
723 QPropertyAlias<T> &operator=(const T &newValue)
724 {
725 setValue(newValue);
726 return *this;
727 }
728
729 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
730 {
731 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
732 }
733
734 bool setBinding(const QUntypedPropertyBinding &newBinding)
735 {
736 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
737 }
738
739#ifndef Q_CLANG_QDOC
740 template <typename Functor>
741 QPropertyBinding<T> setBinding(Functor &&f,
742 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
743 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
744 {
745 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
746 }
747#else
748 template <typename Functor>
749 QPropertyBinding<T> setBinding(Functor f);
750#endif
751
752 bool hasBinding() const
753 {
754 return QBindable<T>(aliasedProperty(), iface).hasBinding();
755 }
756
757 QPropertyBinding<T> binding() const
758 {
759 return QBindable<T>(aliasedProperty(), iface).binding();
760 }
761
762 QPropertyBinding<T> takeBinding()
763 {
764 return QBindable<T>(aliasedProperty(), iface).takeBinding();
765 }
766
767 template<typename Functor>
768 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
769 {
770 return QBindable<T>(aliasedProperty(), iface).onValueChanged(f);
771 }
772
773 template<typename Functor>
774 QPropertyChangeHandler<Functor> subscribe(Functor f)
775 {
776 return QBindable<T>(aliasedProperty(), iface).subscribe(f);
777 }
778
779 bool isValid() const
780 {
781 return aliasedProperty() != nullptr;
782 }
783};
784
785struct QBindingStatus;
786
787struct QBindingStorageData;
788class Q_CORE_EXPORT QBindingStorage
789{
790 mutable QBindingStorageData *d = nullptr;
791 QBindingStatus *bindingStatus = nullptr;
792
793 template<typename Class, typename T, auto Offset, auto Setter>
794 friend class QObjectCompatProperty;
795public:
796 QBindingStorage();
797 ~QBindingStorage();
798
799 bool isEmpty() { return !d; }
800
801 void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const;
802 QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const;
803 QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create);
804};
805
806
807template<typename Class, typename T, auto Offset, auto Signal = nullptr>
808class QObjectBindableProperty : public QPropertyData<T>
809{
810 using ThisType = QObjectBindableProperty<Class, T, Offset, Signal>;
811 static bool constexpr HasSignal = !std::is_same_v<decltype(Signal), std::nullptr_t>;
812 Class *owner()
813 {
814 char *that = reinterpret_cast<char *>(this);
815 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
816 }
817 const Class *owner() const
818 {
819 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
820 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
821 }
822 static void signalCallBack(QUntypedPropertyData *o)
823 {
824 QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o);
825 if constexpr (HasSignal)
826 (that->owner()->*Signal)();
827 }
828public:
829 using value_type = typename QPropertyData<T>::value_type;
830 using parameter_type = typename QPropertyData<T>::parameter_type;
831 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
832 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
833
834 QObjectBindableProperty() = default;
835 explicit QObjectBindableProperty(const T &initialValue) : QPropertyData<T>(initialValue) {}
836 explicit QObjectBindableProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue)) {}
837 explicit QObjectBindableProperty(const QPropertyBinding<T> &binding)
838 : QObjectBindableProperty()
839 { setBinding(binding); }
840#ifndef Q_CLANG_QDOC
841 template <typename Functor>
842 explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
843 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
844 : QObjectBindableProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
845 {}
846#else
847 template <typename Functor>
848 explicit QObjectBindableProperty(Functor &&f);
849#endif
850
851 parameter_type value() const
852 {
853 qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
854 return this->val;
855 }
856
857 arrow_operator_result operator->() const
858 {
859 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
860 return value();
861 } else if constexpr (std::is_pointer_v<T>) {
862 value();
863 return this->val;
864 } else {
865 return;
866 }
867 }
868
869 parameter_type operator*() const
870 {
871 return value();
872 }
873
874 operator parameter_type() const
875 {
876 return value();
877 }
878
879 void setValue(parameter_type t)
880 {
881 auto *bd = qGetBindingStorage(owner())->bindingData(this);
882 if (bd)
883 bd->removeBinding();
884 if (this->val == t)
885 return;
886 this->val = t;
887 notify(bd);
888 }
889
890 void setValue(rvalue_ref t)
891 {
892 auto *bd = qGetBindingStorage(owner())->bindingData(this);
893 if (bd)
894 bd->removeBinding();
895 if (this->val == t)
896 return;
897 this->val = std::move(t);
898 notify(bd);
899 }
900
901 QObjectBindableProperty &operator=(rvalue_ref newValue)
902 {
903 setValue(std::move(newValue));
904 return *this;
905 }
906
907 QObjectBindableProperty &operator=(parameter_type newValue)
908 {
909 setValue(newValue);
910 return *this;
911 }
912
913 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
914 {
915 QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
916 QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
917 notify(bd);
918 return static_cast<QPropertyBinding<T> &>(oldBinding);
919 }
920
921 bool setBinding(const QUntypedPropertyBinding &newBinding)
922 {
923 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
924 return false;
925 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
926 return true;
927 }
928
929#ifndef Q_CLANG_QDOC
930 template <typename Functor>
931 QPropertyBinding<T> setBinding(Functor &&f,
932 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
933 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
934 {
935 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
936 }
937#else
938 template <typename Functor>
939 QPropertyBinding<T> setBinding(Functor f);
940#endif
941
942 bool hasBinding() const
943 {
944 auto *bd = qGetBindingStorage(owner())->bindingData(this);
945 return bd && bd->binding() != nullptr;
946 }
947
948 QPropertyBinding<T> binding() const
949 {
950 auto *bd = qGetBindingStorage(owner())->bindingData(this);
951 return static_cast<QPropertyBinding<T> &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr));
952 }
953
954 QPropertyBinding<T> takeBinding()
955 {
956 return setBinding(QPropertyBinding<T>());
957 }
958
959 template<typename Functor>
960 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
961 {
962 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
963 return QPropertyChangeHandler<Functor>(*this, f);
964 }
965
966 template<typename Functor>
967 QPropertyChangeHandler<Functor> subscribe(Functor f)
968 {
969 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
970 f();
971 return onValueChanged(f);
972 }
973
974 const QtPrivate::QPropertyBindingData &bindingData() const
975 {
976 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
977 return *storage->bindingData(const_cast<ThisType *>(this), true);
978 }
979private:
980 void notify(const QtPrivate::QPropertyBindingData *binding)
981 {
982 if (binding)
983 binding->notifyObservers(this);
984 if constexpr (HasSignal)
985 (owner()->*Signal)();
986 }
987};
988
989#define Q_OBJECT_BINDABLE_PROPERTY(Class, Type, name, ...) \
990 static constexpr size_t _qt_property_##name##_offset() { \
991 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
992 return offsetof(Class, name); \
993 QT_WARNING_POP \
994 } \
995 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
996
997template<typename Class, typename T, auto Offset, auto Getter>
998class QObjectComputedProperty : public QUntypedPropertyData
999{
1000 Class *owner()
1001 {
1002 char *that = reinterpret_cast<char *>(this);
1003 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1004 }
1005 const Class *owner() const
1006 {
1007 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
1008 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1009 }
1010
1011public:
1012 using value_type = T;
1013 using parameter_type = T;
1014
1015 QObjectComputedProperty() = default;
1016
1017 parameter_type value() const
1018 {
1019 qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
1020 return (owner()->*Getter)();
1021 }
1022
1023 std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, parameter_type, void>
1024 operator->() const
1025 {
1026 if constexpr (QTypeTraits::is_dereferenceable_v<T>)
1027 return value();
1028 else
1029 return;
1030 }
1031
1032 parameter_type operator*() const
1033 {
1034 return value();
1035 }
1036
1037 operator parameter_type() const
1038 {
1039 return value();
1040 }
1041
1042 constexpr bool hasBinding() const { return false; }
1043
1044 template<typename Functor>
1045 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1046 {
1047 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1048 return QPropertyChangeHandler<Functor>(*this, f);
1049 }
1050
1051 template<typename Functor>
1052 QPropertyChangeHandler<Functor> subscribe(Functor f)
1053 {
1054 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1055 f();
1056 return onValueChanged(f);
1057 }
1058
1059 QtPrivate::QPropertyBindingData &bindingData() const
1060 {
1061 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1062 return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
1063 }
1064
1065private:
1066};
1067
1068#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
1069 static constexpr size_t _qt_property_##name##_offset() { \
1070 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1071 return offsetof(Class, name); \
1072 QT_WARNING_POP \
1073 } \
1074 QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
1075
1076QT_END_NAMESPACE
1077
1078#endif // QPROPERTY_H
1079