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 | |
64 | QT_BEGIN_NAMESPACE |
65 | |
66 | template <typename T> |
67 | class QPropertyData : public QUntypedPropertyData |
68 | { |
69 | protected: |
70 | mutable T val = T(); |
71 | private: |
72 | class DisableRValueRefs {}; |
73 | protected: |
74 | static constexpr bool UseReferences = !(std::is_arithmetic_v<T> || std::is_enum_v<T> || std::is_pointer_v<T>); |
75 | public: |
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 | |
92 | struct 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 | |
110 | template <typename Functor> class QPropertyChangeHandler; |
111 | class QPropertyBindingErrorPrivate; |
112 | |
113 | class Q_CORE_EXPORT QPropertyBindingError |
114 | { |
115 | public: |
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 | |
136 | private: |
137 | QSharedDataPointer<QPropertyBindingErrorPrivate> d; |
138 | }; |
139 | |
140 | class Q_CORE_EXPORT QUntypedPropertyBinding |
141 | { |
142 | public: |
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); |
161 | private: |
162 | friend class QtPrivate::QPropertyBindingData; |
163 | friend class QPropertyBindingPrivate; |
164 | template <typename> friend class QPropertyBinding; |
165 | QPropertyBindingPrivatePtr d; |
166 | }; |
167 | |
168 | template <typename PropertyType> |
169 | class 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 | |
188 | public: |
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 | |
207 | namespace 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 | |
216 | struct QPropertyObserverPrivate; |
217 | struct QPropertyObserverPointer; |
218 | |
219 | class Q_CORE_EXPORT QPropertyObserver |
220 | { |
221 | public: |
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 | |
241 | protected: |
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 | |
251 | private: |
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 | |
275 | template <typename Functor> |
276 | class [[nodiscard]] QPropertyChangeHandler : public QPropertyObserver |
277 | { |
278 | Functor m_handler; |
279 | public: |
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 | |
301 | template <typename T> |
302 | class 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 | |
314 | public: |
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; } |
454 | private: |
455 | void notify() |
456 | { |
457 | d.notifyObservers(this); |
458 | } |
459 | |
460 | Q_DISABLE_COPY_MOVE(QProperty) |
461 | }; |
462 | |
463 | namespace 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 | |
474 | namespace QtPrivate |
475 | { |
476 | |
477 | struct 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 | |
495 | template<typename Property, typename = void> |
496 | class QBindableInterfaceForProperty |
497 | { |
498 | using T = typename Property::value_type; |
499 | public: |
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 | |
516 | template<typename Property> |
517 | class QBindableInterfaceForProperty<Property, std::void_t<decltype(std::declval<Property>().binding())>> |
518 | { |
519 | using T = typename Property::value_type; |
520 | public: |
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 | |
540 | class QUntypedBindable |
541 | { |
542 | protected: |
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 | |
549 | public: |
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 | |
607 | template<typename T> |
608 | class 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 | {} |
615 | public: |
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 | |
667 | template<typename T> |
668 | class QPropertyAlias : public QPropertyObserver |
669 | { |
670 | Q_DISABLE_COPY_MOVE(QPropertyAlias) |
671 | const QtPrivate::QBindableInterface *iface = nullptr; |
672 | |
673 | public: |
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 | |
785 | struct QBindingStatus; |
786 | |
787 | struct QBindingStorageData; |
788 | class 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; |
795 | public: |
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 | |
807 | template<typename Class, typename T, auto Offset, auto Signal = nullptr> |
808 | class 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 | } |
828 | public: |
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 | } |
979 | private: |
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 | |
997 | template<typename Class, typename T, auto Offset, auto Getter> |
998 | class 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 | |
1011 | public: |
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 | |
1065 | private: |
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 | |
1076 | QT_END_NAMESPACE |
1077 | |
1078 | #endif // QPROPERTY_H |
1079 | |