Qx v0.6
Qt Extensions Library
Loading...
Searching...
No Matches
qx-property.h
Go to the documentation of this file.
1#ifndef QX_PROPERTY_H
2#define QX_PROPERTY_H
3
4// Shared Lib Support
5#include "qx/core/qx_core_export.h"
6
7// Unit Includes
8#include "__private/qx-property_detail.h"
9
10// Standard Library Includes
11#include <concepts>
12#include <functional>
13#include <utility>
14
15// Qt Includes
16#include <QtGlobal>
17
18// Extra-component Includes
21
22/* TODO: In general, utilize more non-template base types to reduce code bloat
23 * and hide away implementation details like how the Qt Bindable Property system
24 * does
25 */
26
27namespace Qx
28{
29
30template<typename T>
31class Bindable;
32
33/* TODO: Ideally, this class should be marked as nodiscard directly, as it prevents the need to repeat the
34 * diagnostic string on each function that requires an instance of this, and ensures that the diagnostic
35 * is used in a discard situation even for user functions; however, we must use the C++11 style attribute
36 * for that here, and that conflicts with our export macro as you cannot mix the old GNU attribute
37 * syntax with the newer C++11 standard syntax. Unfortunately, CMake's GenerateExportHeader module does not
38 * support using the newer syntax so for now we're SOL unless we start generating the export header ourselves,
39 * which should be avoided in the hopes that eventually CMake adds this functionality.
40 *
41 * If we really get desperate a hacky workaround could be adding an extra step to search/replace the older
42 * syntax in the initial header with the newer equivalent, though that's also complicated.
43 *
44 * For now we just use nodiscard on each method that returns the type, which dodges the issue since they are
45 * all in a template class (no export required).
46 */
47class QX_CORE_EXPORT PropertyNotifier
48{
49 template<typename T>
50 friend class AbstractBindableProperty;
51 Q_DISABLE_COPY(PropertyNotifier);
52//-Aliases-------------------------------------------------------------
53private:
54 using ManagerPtr = std::shared_ptr<_QxPrivate::PropertyObserverManager>;
55 using WeakManagerPtr = std::weak_ptr<_QxPrivate::PropertyObserverManager>;
56 using ObserverId = _QxPrivate::PropertyObserverManager::ObserverId;
57
58//-Instance Variables-------------------------------------------------------------
59private:
60 WeakManagerPtr mManager;
61 ObserverId mId;
62
63//-Constructor-------------------------------------------------------------
64private:
65 PropertyNotifier(const ManagerPtr& manager, ObserverId id);
66
67public:
68 PropertyNotifier(PropertyNotifier&& other) noexcept;
69
70//-Destructor-------------------------------------------------------------
71public:
73
74//-Operators-------------------------------------------------------------
75public:
76 PropertyNotifier& operator=(PropertyNotifier&& other) noexcept;
77};
78
79template<typename T>
81{
82 /* This is basically just a wrapper around std::function<T()> for a more application
83 * specific way to pass around binding function if desired.
84 */
85//-Instance Variables-------------------------------------------------------------
86private:
87 std::function<T()> mFunctor;
88
89//-Constructor--------------------------------------------------------------------
90public:
91 PropertyBinding() = default;
92
93 template<std::invocable Functor>
94 PropertyBinding(Functor&& f) :
95 mFunctor(std::forward<Functor>(f))
96 {}
97
98//-Instance Functions-----------------------------------------------------------
99public:
100 bool isNull() const { return !static_cast<bool>(mFunctor); }
101
102//-Operator----------------------------------------------------------------------
103public:
104 explicit operator bool() const { return !isNull(); }
105 T operator()() const { return mFunctor(); }
106};
107
108template<typename T>
109class AbstractBindableProperty : private _QxPrivate::BindableInterface
110{
111 Q_DISABLE_COPY(AbstractBindableProperty);
112//-Aliases---------------------------------------------------------------------
113private:
114 using ObserverManager = _QxPrivate::PropertyObserverManager;
115
116//-Instance Variables-------------------------------------------------------------
117private:
118 PropertyBinding<T> mBinding;
119 std::shared_ptr<ObserverManager> mObserverManager;
120 // ^ This being dynamic keeps its address stable even when 'this' is moved
121
122//-Constructor-----------------------------------------------------------------
123protected:
124 /* We cannot handle the intake of a possible binding from a
125 * derived ctor here as notifyBindingAdded() will lead to
126 * valueBypassingBindings() being called, which is a call of a
127 * polymorphic function from within the base class where that function
128 * is declared. This is UB since construction of the derived instance
129 * won't have begun at that point yet and so the vtable for the instance
130 * will just point to the Base and cannot see the derived overload.
131 *
132 * So instead, each derived needs to intake the binding and call
133 * setBinding() manually if it has a ctor that takes a binding function
134 *
135 * template<typename BindingT = std::function<T()>>
136 * AbstractBindableProperty(BindingT&& f = {}) :
137 * mObserverManager(new ObserverManager),
138 * mBinding(std::forward<BindingT>(f))
139 * {
140 * if(mBinding)
141 * notifyBindingAdded();
142 * }
143 */
145 mObserverManager(new ObserverManager)
146 {}
147
149
150//-Instance Functions-----------------------------------------------------------
151private:
152 inline bool valueSame(const T& value) const
153 {
154 if constexpr(defines_equality_s<T>)
155 return value == valueBypassingBindings();
156
157 return false;
158 }
159
160 template<typename U>
161 requires std::same_as<std::remove_cvref_t<U>, T>
162 bool updateIfDifferent(U&& newValue)
163 {
164 if(valueSame(newValue))
165 return false;
166
167 setValueBypassingBindings(std::forward<U>(newValue));
168 return true;
169 }
170
171 template<typename Binding>
172 PropertyBinding<T> cycleBinding(Binding&& b)
173 {
174 auto oldBinding = std::exchange(mBinding, std::forward<Binding>(b));
175 if(oldBinding)
176 notifyBindingRemoved();
177 if(mBinding)
178 notifyBindingAdded();
179
180 return oldBinding;
181 }
182
183 bool callBinding() override
184 {
185 Q_ASSERT(mBinding);
186 return updateIfDifferent(mBinding());
187 }
188
189 void notifyObservers() const override { mObserverManager->invokeAll(); }
190
191public:
192 virtual void setValueBypassingBindings(T&& v) = 0;
193
194 /* This is a bit of a blemish. Originally, this was a pure virtual function like the T&& version
195 * in order to let implementations optimize their copy assignment to the greatest extent possible;
196 * however, as soon as the method is virtual the compiler tries to instantiate it derivatives always,
197 * regardless of T, which means that properties would stop working with move-only types (unacceptable).
198 * Ideally we'd just constrain the virtual method, but even though technically it's possible (GCC allows it),
199 * it's not allowed by the standard. So instead we implement it here as a manual copy/move, delegating to the
200 * T&& version. In most cases the compiler should be able to optimize this to be equivalent to a direct copy
201 * assignment (assuming that's what the derived class did), and in other cases it should be barely more
202 * expensive. Regardless, this is something to try and work around as soon as any other class hierarchy or
203 * approach changes make a better way possible, or if the standard every allows constraining virtual methods.
204 */
205 void setValueBypassingBindings(const T& v) requires std::copyable<T> { setValueBypassingBindings(T(v)); }
206
207 virtual const T& valueBypassingBindings() const = 0;
208
209 PropertyBinding<T> binding() const { return mBinding; }
210 [[nodiscard]] PropertyBinding<T> takeBinding() { return cycleBinding(PropertyBinding<T>()); }
211 void removeBinding() { if(hasBinding()) Q_UNUSED(takeBinding()); }
212
213 template<std::invocable Functor>
214 PropertyBinding<T> setBinding(Functor&& f) { return cycleBinding(std::forward<Functor>(f)); }
215
217 bool hasBinding() const { return !mBinding.isNull(); }
218
219 const T& value() const
220 {
221 attachToCurrentEval();
222 return valueBypassingBindings();
223 }
224
225 void setValue(const T& newValue)
226 {
228 if(updateIfDifferent(newValue))
229 notifyValueChanged();
230 }
231
232 void setValue(T&& newValue)
233 {
235 if(updateIfDifferent(std::move(newValue)))
236 notifyValueChanged();
237 }
238
239 template<std::invocable Functor>
240 [[nodiscard("The functor will never be called if PropertyNotifier is discarded!")]] PropertyNotifier addNotifier(Functor&& f) const
241 {
242 auto id = mObserverManager->add(std::forward<Functor>(f));
243 return PropertyNotifier(mObserverManager, id);
244 }
245
246 template<std::invocable Functor>
247 void addLifetimeNotifier(Functor&& f) const { mObserverManager->add(std::forward<Functor>(f)); }
248
249 template<std::invocable Functor>
250 [[nodiscard("The functor will never be called if PropertyNotifier is discarded!")]] PropertyNotifier subscribe(Functor&& f) const
251 {
252 f();
253 return addNotifier(std::forward<Functor>(f));
254 }
255
256 template<std::invocable Functor>
257 void subscribeLifetime(Functor&& f) const
258 {
259 f();
260 addLifetimeNotifier(std::forward<Functor>(f));
261 }
262
263//-Operators-------------------------------------------------------------
264protected:
266
267public:
268 decltype(auto) operator->() const requires arrowable_container_type<T>
269 {
271 }
272
273 const T& operator*() const { return value(); }
274 operator const T&() const { return value(); }
275};
276
277} // namespace Qx
278
279namespace _QxPrivate
280{
281
282template<typename T>
283class ObjectPropertyAdapter final : private Qx::AbstractBindableProperty<T>
284{
285 friend class Qx::Bindable<T>;
286 Q_DISABLE_COPY_MOVE(ObjectPropertyAdapter);
287
288//-Base Forwards------------------------------------------------------------------
289private:
291
292//-Instance Variables-------------------------------------------------------------
293private:
294 QObject* mObject;
295 QMetaProperty mProperty;
296 ObjectPropertyAdapterLiaison mLiaison;
297 T mCache;
298 bool mBlockPropertyUpdate;
299
300 /* NOTE: The guards used here set themselves, perform the operation, and then
301 * unset themselves. This blocks recursive updates to values (i.e. a second
302 * update comes in before the fist has unwound and then is blocked), which
303 * might be of a type that shouldn't actually be blocked( this would be most
304 * likely to happen due to a user installed callback that fires at the end
305 * of an update wave); however, I believe that any case in which this occurs
306 * would be a property dependency cycle, which is not allowed and caught anyway.
307 *
308 * But, if it turns out there are valid cases where recursive updates should
309 * happen here, just switch to a "ignore once" model where the flag is cleared
310 * right after its checked (if it was high), instead of being cleared by the initial
311 * caller that set it high (as is now).
312 */
313
314//-Constructor-----------------------------------------------------------------
315private:
316 ObjectPropertyAdapter(QObject* obj, const QMetaProperty& property) :
317 mObject(nullptr),
318 mProperty(property),
319 mBlockPropertyUpdate(false)
320 {
321 // Checks
322 auto adaptedMetaType = QMetaType::fromType<T>(); // works even if the type isn't registered
323 auto propertyMetaType = property.metaType();
324 if(propertyMetaType != adaptedMetaType)
325 {
326 qWarning("Qx::ObjectPropertyAdapter: The type of property %s, %s, is not the same as the"
327 "adapter type, %s.", property.name(), propertyMetaType.name(), adaptedMetaType.name());
328 return;
329 }
330
331 if(!property.hasNotifySignal())
332 {
333 qWarning("Qx::ObjectPropertyAdapter: Property %s has no notify signal.", property.name());
334 return;
335 }
336
337 // Setup
338 if(Q_UNLIKELY(!mLiaison.configure(obj, property)))
339 return;
340
341 QObject::connect(&mLiaison, &ObjectPropertyAdapterLiaison::objectDeleted, &mLiaison, [this]{
342 /* NOTE: We die when the object we're adapting dies. This means we should
343 * never leak since the destroyed() signals is never blocked.
344 *
345 * SINCE WE SELF-DELETE HERE, DO NOT USE THE OBJECT AFTER THIS IN ANY WAY
346 */
347 delete this;
348 });
349 QObject::connect(&mLiaison, &ObjectPropertyAdapterLiaison::propertyNotified, &mLiaison, [this]{
350 handleExternalUpdate();
351 });
352
353 mObject = obj;
354 mCache = readProperty();
355 }
356
357//-Destructor-----------------------------------------------------------------
358public:
359 ~ObjectPropertyAdapter()
360 {
361 if(isValid())
362 ObjectPropertyAdapterRegistry::instance()->remove(mObject, mProperty);
363 }
364
365//-Class Functions-----------------------------------------------------------
366private:
367 static bool basicInputValidation(QObject* obj, const QMetaProperty& property)
368 {
369 /* This does not validate that the property is fully valid to be a bindable,
370 * but confirms that the inputs are present and that the property at least
371 * belongs to the object. This is so that we can be sure the inputs are valid
372 * enough for the purposes of checking if an adapter is already in the store,
373 * which means we don't need to perform all validation steps if an adapter for
374 * these inputs was already created.
375 */
376 if(!obj)
377 {
378 qWarning("Qx::ObjectPropertyAdapter: Null object provided.");
379 return false;
380 }
381
382 if(!property.isValid())
383 {
384 qWarning("Qx::ObjectPropertyAdapter: Invalid property provided.");
385 return false;
386 }
387
388 /* Since there is only one MetaObject per type, the address for the provided properties name
389 * should be identical to the name address if we look it up through the provided object. This
390 * proves that the provided property is one of the objects properties
391 */
392 if(obj->metaObject()->property(property.propertyIndex()).name() != property.name())
393 {
394 qWarning("Qx::ObjectPropertyAdapter: The provided property does not belong to the provided object.");
395 return false;
396 }
397
398 return true;
399 }
400
401 static ObjectPropertyAdapter* get(QObject* obj, const QMetaProperty& property)
402 {
403 if(!basicInputValidation(obj, property))
404 return nullptr;
405
406 auto man = ObjectPropertyAdapterRegistry::instance();
407 ObjectPropertyAdapter* adptr = static_cast<ObjectPropertyAdapter*>(man->retrieve(obj, property));
408 if(!adptr)
409 {
410 auto newAdptr = new ObjectPropertyAdapter(obj, property);
411 if(newAdptr->isValid())
412 {
413 man->store(obj, property, newAdptr);
414 adptr = newAdptr;
415 }
416 else
417 delete newAdptr;
418 }
419
420 return adptr;
421 }
422
423//-Instance Functions-----------------------------------------------------------
424private:
425 bool isValid() const { return mObject; }
426 T readProperty()
427 {
428 QVariant value = mProperty.read(mObject);
429 Q_ASSERT(value.isValid() && value.canConvert<T>());
430 return value.value<T>();
431 }
432
433 void writeProperty(const T& value)
434 {
435 if(mBlockPropertyUpdate)
436 return;
437
438 // When we write to the property, we want to ignore property update notifications obviously
439 mLiaison.setIgnoreUpdates(true);
440 bool wrote = mProperty.write(mObject, value);
441 Q_ASSERT(wrote);
442 mLiaison.setIgnoreUpdates(false);
443 }
444
445 void handleExternalUpdate()
446 {
447 /* Treat the external update as if one directly use setValue() on this property using the new value,
448 * but skip updating the underlying Q_PROPERTY
449 */
450 mBlockPropertyUpdate = true;
451 setValue(readProperty());
452 mBlockPropertyUpdate = false;
453 }
454
455public:
456 using Qx::AbstractBindableProperty<T>::setValueBypassingBindings;
457 void setValueBypassingBindings(T&& v) override
458 {
459 mCache = std::move(v);
460 writeProperty(mCache);
461 }
462
463 const T& valueBypassingBindings() const override { return mCache; }
464 bool isPropertyWriteable() const { return mProperty.isWritable(); }
465};
466
467} // namespace _QxPrivate
468
469namespace Qx
470{
471
472template<typename T>
474{
475 /* AbstractBindableProperty does the heavy lifting, this is basically just a shell
476 * that forwards method calls. A little silly, but worth it in order to have a unified
477 * interface object that does not rely on using pointers in user code (and of course
478 * this mirrors Qt properties :)).
479 *
480 * To be fair, it also accounts for the peculiarities of QObject based properties
481 * which can be invalid, read-only, etc, and has a specialized constructor that
482 * hides the QObject specific adapter internally.
483 */
484
485//-Aliases-------------------------------------------------------------
486private:
488
489//-Instance Variables-------------------------------------------------------------
490private:
491 WrappedProperty* mBindable;
492 bool mReadOnly;
493
494//-Constructor-----------------------------------------------------------------
495public:
497 mBindable(nullptr),
498 mReadOnly(true)
499 {}
500
502 mBindable(&bp),
503 mReadOnly(false)
504 {}
505
507 mBindable(const_cast<AbstractBindableProperty<T>*>(&bp)),
508 mReadOnly(true)
509 {}
510
511 Bindable(QObject* obj, const QMetaProperty& property) :
512 mBindable(nullptr),
513 mReadOnly(true)
514 {
515 auto adptr = _QxPrivate::ObjectPropertyAdapter<T>::get(obj, property);
516 if(!adptr)
517 return;
518
519 mReadOnly = !adptr->isPropertyWriteable();
520 mBindable = adptr;
521 }
522
523 Bindable(QObject* obj, const char* property) : Bindable(obj, [=]{
524 if (!obj)
525 return QMetaProperty{};
526 auto propertyIndex = obj->metaObject()->indexOfProperty(property);
527 if (propertyIndex < 0)
528 {
529 qWarning("Qx::Bindable: No property named %s for QObject bindable (obj = %p).", property, obj);
530 return QMetaProperty{};
531 }
532 return obj->metaObject()->property(propertyIndex);
533 }())
534 {}
535
536//-Instance Functions-------------------------------------------------------------
537private:
538 bool mutableCheck() const
539 {
540 if(mReadOnly)
541 {
542 qWarning("Qx::Bindable: Attempt to write/mutate read-only property through Bindable (%p).", this);
543 return false;
544 }
545
546 return true;
547 }
548
549public:
550 // Forwards
552 {
553 Q_ASSERT(mBindable);
554 if(!mutableCheck())
555 return;
556
557 mBindable->setValueBypassingBindings(v);
558 }
559
561 {
562 Q_ASSERT(mBindable);
563 if(!mutableCheck())
564 return;
565
566 mBindable->setValueBypassingBindings(std::forward<T>(v));
567 }
568
569 const T& valueBypassingBindings() const { Q_ASSERT(mBindable); return mBindable->valueBypassingBindings(); }
570 PropertyBinding<T> binding() const { Q_ASSERT(mBindable); return mBindable->binding(); }
571
573 {
574 Q_ASSERT(mBindable);
575 if(!mutableCheck())
576 return {};
577
578 return mBindable->takeBinding();
579 }
580
582 {
583 Q_ASSERT(mBindable);
584 if(!mutableCheck())
585 return;
586
587 mBindable->removeBinding();
588 }
589
590 template<std::invocable Functor>
592 {
593 Q_ASSERT(mBindable);
594 if(!mutableCheck())
595 return {};
596
597 return mBindable->setBinding(std::forward<Functor>(f));
598 }
599
601 {
602 Q_ASSERT(mBindable);
603 if(!mutableCheck())
604 return {};
605
606 return mBindable->setBinding(binding);
607 }
608
609 bool hasBinding() const { Q_ASSERT(mBindable); return mBindable->hasBinding(); }
610 const T& value() const { Q_ASSERT(mBindable); return mBindable->value(); }
611
612 void setValue(const T& newValue)
613 {
614 Q_ASSERT(mBindable);
615 if(!mutableCheck())
616 return;
617
618 mBindable->setValue(newValue);
619 }
620
621 void setValue(T&& newValue)
622 {
623 Q_ASSERT(mBindable);
624 if(!mutableCheck())
625 return;
626
627 mBindable->setValue(std::forward<T>(newValue));
628 }
629
630 template<std::invocable Functor>
631 [[nodiscard("The functor will never be called if PropertyNotifier is discarded!")]] PropertyNotifier addNotifier(Functor&& f) const
632 {
633 Q_ASSERT(mBindable);
634 return mBindable->addNotifier(std::forward<Functor>(f));
635 }
636
637 template<std::invocable Functor>
638 void addLifetimeNotifier(Functor&& f) const { Q_ASSERT(mBindable); mBindable->addLifetimeNotifier(std::forward<Functor>(f)); }
639
640 template<std::invocable Functor>
641 [[nodiscard("The functor will never be called if PropertyNotifier is discarded!")]] PropertyNotifier subscribe(Functor&& f) const
642 {
643 Q_ASSERT(mBindable);
644 return mBindable->subscribe(std::forward<Functor>(f));
645 }
646
647 template<std::invocable Functor>
648 void subscribeLifetime(Functor&& f) const { Q_ASSERT(mBindable); mBindable->subscribeLifetime(std::forward<Functor>(f)); }
649
650 // Bindable specific stuff
651 bool isValid() const { return mBindable; }
652 bool isReadOnly() const { return mReadOnly; }
653
654//-Operators-------------------------------------------------------------
655public:
656 decltype(auto) operator->() const requires defines_member_ptr<WrappedProperty>
657 {
658 Q_ASSERT(mBindable);
659 return mBindable->operator->();
660 }
661
662 const T& operator*() const { Q_ASSERT(mBindable); return mBindable->operator*(); }
663 operator const T&() const { Q_ASSERT(mBindable); return static_cast<const T&>(*mBindable); }
664
665 Bindable& operator=(T&& newValue) noexcept
666 {
667 Q_ASSERT(mBindable);
668 if(!mutableCheck())
669 return *this;
670
671 mBindable->setValue(std::forward<T>(newValue));
672 return *this;
673 }
674
675 Bindable& operator=(const T& newValue) noexcept
676 {
677 Q_ASSERT(mBindable);
678 if(!mutableCheck())
679 return *this;
680
681 mBindable->setValue(newValue);
682 return *this;
683 }
684};
685
686template<typename T>
688{
689 // Basic property, basically just wraps data
690 Q_DISABLE_COPY(Property);
691//-Instance Variables-------------------------------------------------------------
692private:
693 T mData;
694
695//-Constructor-----------------------------------------------------------------
696public:
697 Property() : mData(T()) {}
698
699 // TODO: QProperty can't be moved, should we disallow this?
700 Property(Property&& other) noexcept { *this = std::move(other); }
701
702 template<std::invocable Functor>
703 Property(Functor&& f) { AbstractBindableProperty<T>::setBinding(std::forward<Functor>(f)); }
704
706
707 Property(T&& initialValue) :
708 mData(std::forward<T>(initialValue))
709 {}
710
711 Property(const T& initialValue) :
712 mData(initialValue)
713 {}
714
715//-Instance Functions-------------------------------------------------------------
716public:
718 void setValueBypassingBindings(T&& v) override { mData = std::move(v); }
719 const T& valueBypassingBindings() const override { return mData; }
720
721//-Operators-------------------------------------------------------------
722public:
723 Property& operator=(Property&& other) noexcept
724 {
725 if(&other != this)
726 {
727 AbstractBindableProperty<T>::operator=(std::move(other)); // Move base
728 mData = std::exchange(other.mData, {});
729 }
730
731 return *this;
732 }
733
734 Property& operator=(T&& newValue) { AbstractBindableProperty<T>::setValue(std::forward<T>(newValue)); return *this; }
735 Property& operator=(const T& newValue) { AbstractBindableProperty<T>::setValue(newValue); return *this; }
736};
737
738//-Namespace Functions-------------------------------------------------------------
739QX_CORE_EXPORT void beginPropertyUpdateGroup();
740QX_CORE_EXPORT void endPropertyUpdateGroup();
741
742//-Classes (cont.)----------------------------------------------------------------
744{
745 Q_DISABLE_COPY_MOVE(ScopedPropertyUpdateGroup);
746public:
749};
750
751}
752
753#endif // QX_PROPERTY_H
The AbstractBindableProperty class provides the baseline feature for bindable properties of the Qx Bi...
Definition qx-property.h:110
PropertyBinding< T > setBinding(Functor &&f)
Definition qx-property.h:214
void subscribeLifetime(Functor &&f) const
Definition qx-property.h:257
virtual void setValueBypassingBindings(T &&v)=0
void setValue(T &&newValue)
Definition qx-property.h:232
void setValue(const T &newValue)
Definition qx-property.h:225
const T & operator*() const
Definition qx-property.h:273
bool hasBinding() const
Definition qx-property.h:217
const T & value() const
Definition qx-property.h:219
PropertyNotifier addNotifier(Functor &&f) const
Definition qx-property.h:240
PropertyNotifier subscribe(Functor &&f) const
Definition qx-property.h:250
virtual const T & valueBypassingBindings() const =0
PropertyBinding< T > setBinding(const PropertyBinding< T > &binding)
Definition qx-property.h:216
PropertyBinding< T > takeBinding()
Definition qx-property.h:210
AbstractBindableProperty()
Definition qx-property.h:144
void addLifetimeNotifier(Functor &&f) const
Definition qx-property.h:247
void removeBinding()
Definition qx-property.h:211
void setValueBypassingBindings(const T &v)
Definition qx-property.h:205
PropertyBinding< T > binding() const
Definition qx-property.h:209
AbstractBindableProperty & operator=(AbstractBindableProperty &&other) noexcept=default
AbstractBindableProperty(AbstractBindableProperty &&other) noexcept=default
Bindable is a wrapper class around binding-enabled properties that provides uniform access,...
Definition qx-property.h:474
const T & value() const
Definition qx-property.h:610
void setValueBypassingBindings(T &&v)
Definition qx-property.h:560
Bindable(QObject *obj, const QMetaProperty &property)
Definition qx-property.h:511
const T & valueBypassingBindings() const
Definition qx-property.h:569
bool hasBinding() const
Definition qx-property.h:609
Bindable & operator=(const T &newValue) noexcept
Definition qx-property.h:675
PropertyBinding< T > setBinding(const PropertyBinding< T > &binding)
Definition qx-property.h:600
Bindable(AbstractBindableProperty< T > &bp)
Definition qx-property.h:501
Bindable(const AbstractBindableProperty< T > &bp)
Definition qx-property.h:506
Bindable()
Definition qx-property.h:496
void subscribeLifetime(Functor &&f) const
Definition qx-property.h:648
Bindable(QObject *obj, const char *property)
Definition qx-property.h:523
void setValue(const T &newValue)
Definition qx-property.h:612
bool isValid() const
Definition qx-property.h:651
PropertyBinding< T > binding() const
Definition qx-property.h:570
Bindable & operator=(T &&newValue) noexcept
Definition qx-property.h:665
void addLifetimeNotifier(Functor &&f) const
Definition qx-property.h:638
void removeBinding()
Definition qx-property.h:581
void setValueBypassingBindings(const T &v)
Definition qx-property.h:551
PropertyNotifier addNotifier(Functor &&f) const
Definition qx-property.h:631
PropertyBinding< T > takeBinding()
Definition qx-property.h:572
PropertyNotifier subscribe(Functor &&f) const
Definition qx-property.h:641
void setValue(T &&newValue)
Definition qx-property.h:621
bool isReadOnly() const
Definition qx-property.h:652
const T & operator*() const
Definition qx-property.h:662
PropertyBinding< T > setBinding(Functor &&f)
Definition qx-property.h:591
The PropertyBinding class acts as a functor for properties with automatic property bindings.
Definition qx-property.h:81
PropertyBinding()=default
PropertyBinding(Functor &&f)
Definition qx-property.h:94
T operator()() const
Definition qx-property.h:105
bool isNull() const
Definition qx-property.h:100
The PropertyNotifier class controls the lifecycle of a change callback installed on a Property.
Definition qx-property.h:48
The Property class is a template class that enables automatic property bindings.
Definition qx-property.h:688
const T & valueBypassingBindings() const override
Definition qx-property.h:719
Property(const T &initialValue)
Definition qx-property.h:711
Property(T &&initialValue)
Definition qx-property.h:707
Property(const PropertyBinding< T > &binding)
Definition qx-property.h:705
Property & operator=(T &&newValue)
Definition qx-property.h:734
Property(Functor &&f)
Definition qx-property.h:703
Property(Property &&other) noexcept
Definition qx-property.h:700
Property & operator=(Property &&other) noexcept
Definition qx-property.h:723
Property & operator=(const T &newValue)
Definition qx-property.h:735
Property()
Definition qx-property.h:697
void setValueBypassingBindings(T &&v) override
Definition qx-property.h:718
The ScopedPropertyUpdateGroup class starts an update group when constructed and ends it when destroye...
Definition qx-property.h:744
~ScopedPropertyUpdateGroup() noexcept(false)
Definition qx-property.h:748
Q_NODISCARD_CTOR ScopedPropertyUpdateGroup()
Definition qx-property.h:747
Definition qx-helpers.h:54
Specifies that a type defines an equal to operator for itself (with strict return).
Definition qx-concepts.h:404
The Qx namespace is the main namespace through which all non-global functionality of the Qx library i...
Definition qx-abstracterror.cpp:13
container_arrow_result< T > container_arrow_operator(T &data)
Definition qx-helpers.h:57
void beginPropertyUpdateGroup()
Definition qx-property.cpp:1524
void endPropertyUpdateGroup()
Definition qx-property.cpp:1533
int indexOfProperty(const char *name) const const
QMetaProperty property(int index) const const
bool hasNotifySignal() const const
bool isValid() const const
bool isWritable() const const
const char * name() const const
int propertyIndex() const const
QVariant read(const QObject *object) const const
bool write(QObject *object, QVariant &&v) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual const QMetaObject * metaObject() const const
The qx-concepts header file provides a library of general purpose concepts as an extension of the sta...
The qx-concepts header file provides a set of various convenience functions that are are designed to ...