summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qproperty.cpp
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-05-25 12:54:51 +0200
committerUlf Hermann <ulf.hermann@qt.io>2020-05-27 14:41:52 +0200
commita64a0ce3317afa1ff633d1d08904c967dbc6ecad (patch)
tree51fe001645681907fbeefce9236809a805d7b4ff /src/corelib/kernel/qproperty.cpp
parent0a07f9528c56615a38b305e8ba8b8b9dc433f260 (diff)
Add a QPropertyAlias
A property alias is the equivalent of the "alias" keyword in QML. It provides the same API as QProperty, but redirects any access to the QProperty it was initialized with. When the original property is destroyed the binding becomes invalid and ignores any further acccess. Task-number: QTBUG-84370 Change-Id: I0aef8d50e73a2aa9e7703d51194d4c5480573578 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/kernel/qproperty.cpp')
-rw-r--r--src/corelib/kernel/qproperty.cpp249
1 files changed, 246 insertions, 3 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 4ed4af4c8e..c3e7711f94 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -254,6 +254,12 @@ QPropertyObserver::QPropertyObserver(void (*callback)(QPropertyObserver *, void
d.setChangeHandler(callback);
}
+QPropertyObserver::QPropertyObserver(void *aliasedPropertyPtr)
+{
+ QPropertyObserverPointer d{this};
+ d.setAliasedProperty(aliasedPropertyPtr);
+}
+
void QPropertyObserver::setSource(QPropertyBase &property)
{
QPropertyObserverPointer d{this};
@@ -303,6 +309,8 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other)
void QPropertyObserverPointer::unlink()
{
+ if (ptr->next.tag() & QPropertyObserver::ObserverNotifiesAlias)
+ ptr->aliasedPropertyPtr = 0;
if (ptr->next)
ptr->next->prev = ptr->prev;
if (ptr->prev)
@@ -317,6 +325,12 @@ void QPropertyObserverPointer::setChangeHandler(void (*changeHandler)(QPropertyO
ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler);
}
+void QPropertyObserverPointer::setAliasedProperty(void *propertyPtr)
+{
+ ptr->aliasedPropertyPtr = quintptr(propertyPtr);
+ ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias);
+}
+
void QPropertyObserverPointer::setBindingToMarkDirty(QPropertyBindingPrivate *binding)
{
ptr->bindingToMarkDirty = binding;
@@ -331,7 +345,8 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
auto observer = const_cast<QPropertyObserver*>(ptr);
while (observer) {
auto * const next = observer->next.data();
- if (observer->next.tag() == QPropertyObserver::ObserverNotifiesChangeHandler) {
+ switch (observer->next.tag()) {
+ case QPropertyObserver::ObserverNotifiesChangeHandler:
if (!knownIfPropertyChanged && triggeringBinding) {
knownIfPropertyChanged = true;
@@ -344,9 +359,13 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
handlerToCall(observer, propertyDataPtr);
observer->changeHandler = handlerToCall;
}
- } else {
+ break;
+ case QPropertyObserver::ObserverNotifiesBinding:
if (observer->bindingToMarkDirty)
observer->bindingToMarkDirty->markDirtyAndNotifyObservers();
+ break;
+ case QPropertyObserver::ObserverNotifiesAlias:
+ break;
}
observer = next;
}
@@ -354,7 +373,8 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
void QPropertyObserverPointer::observeProperty(QPropertyBasePointer property)
{
- unlink();
+ if (ptr->prev)
+ unlink();
property.addObserver(ptr);
}
@@ -697,4 +717,227 @@ QPropertyBindingSourceLocation QPropertyBindingError::location() const
A handler instance can be transferred between C++ scopes using move semantics.
*/
+/*!
+ \class QPropertyAlias
+ \inmodule QtCore
+ \brief The QPropertyAlias class is a safe alias for a QProperty with same template parameter.
+
+ \ingroup tools
+
+ QPropertyAlias\<T\> wraps a pointer to a QProperty\<T\> and automatically
+ invalidates itself when the QProperty\<T\> is destroyed. It forwards all
+ method invocations to the wrapped property. For example:
+
+ \code
+ QProperty<QString> *name = new QProperty<QString>("John");
+ QProperty<int> age(41);
+
+ QPropertyAlias<QString> nameAlias(name);
+ QPropertyAlias<int> ageAlias(&age);
+
+ QPropertyAlias<QString> fullname;
+ fullname.setBinding([&]() { return nameAlias.value() + " age:" + QString::number(ageAlias.value()); });
+
+ qDebug() << fullname.value(); // Prints "Smith age: 41"
+
+ *name = "Emma"; // Marks binding expression as dirty
+
+ qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 41"
+
+ // Birthday is coming up
+ ageAlias.setValue(age.value() + 1); // Writes the age property through the alias
+
+ qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 42"
+
+ delete name; // Leaves the alias in an invalid, but accessible state
+ nameAlias.setValue("Eve"); // Ignored: nameAlias carries a default-constructed QString now
+
+ ageAlias.setValue(92);
+ qDebug() << fullname.value(); // Re-evaluates the binding expression and prints " age: 92"
+ \endcode
+*/
+
+/*!
+ \fn template <typename T> QPropertyAlias<T>::QPropertyAlias(QProperty<T> *property)
+
+ Constructs a property alias for the given \a property.
+*/
+
+/*!
+ \fn template <typename T> explicit QPropertyAlias<T>::QPropertyAlias(QPropertyAlias<T> *alias)
+
+ Constructs a property alias for the property aliased by \a alias.
+*/
+
+/*!
+ \fn template <typename T> T QPropertyAlias<T>::value() const
+
+ Returns the value of the aliased property. This may evaluate a binding
+ expression that is tied to the property, before returning the value.
+*/
+
+/*!
+ \fn template <typename T> QPropertyAlias<T>::operator T() const
+
+ Returns the value of the aliased property. This may evaluate a binding
+ expression that is tied to the property, before returning the value.
+*/
+
+/*!
+ \fn template <typename T> void QPropertyAlias<T>::setValue(const T &newValue)
+
+ Assigns \a newValue to the aliased property and removes the property's
+ associated binding, if present.
+*/
+
+/*!
+ \fn template <typename T> void QPropertyAlias<T>::setValue(T &&newValue)
+ \overload
+
+ Assigns \a newValue to the aliased property and removes the property's
+ associated binding, if present.
+*/
+
+/*!
+ \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const T &newValue)
+
+ Assigns \a newValue to the aliased property and returns a reference to this
+ QPropertyAlias.
+*/
+
+/*!
+ \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(T &&newValue)
+ \overload
+
+ Assigns \a newValue to the aliased property and returns a reference to this
+ QPropertyAlias.
+*/
+
+/*!
+ \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const QPropertyBinding<T> &newBinding)
+ \overload
+
+ Associates the value of the aliased property with the provided \a newBinding
+ expression and returns a reference to this alias. The first time the
+ property value is read, either from the property itself or from any alias, the
+ binding is evaluated. Whenever a dependency of the binding changes, the
+ binding will be re-evaluated the next time the value of this property is read.
+*/
+
+/*!
+ \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::setBinding(const QPropertyBinding<T> &newBinding)
+
+ Associates the value of the aliased property with the provided \a newBinding
+ expression and returns any previous binding the associated with the aliased
+ property. The first time the property value is read, either from the property
+ itself or from any alias, the binding is evaluated. Whenever a dependency of
+ the binding changes, the binding will be re-evaluated the next time the value
+ of this property is read.
+
+ Returns any previous binding associated with the property, or a
+ default-constructed QPropertyBinding<T>.
+*/
+
+/*!
+ \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::setBinding(QPropertyBinding<T> &&newBinding)
+ \overload
+
+ Associates the value of the aliased property with the provided \a newBinding
+ expression and returns any previous binding the associated with the aliased
+ property. The first time the property value is read, either from the property
+ itself or from any alias, the binding is evaluated. Whenever a dependency of
+ the binding changes, the binding will be re-evaluated the next time the value
+ of this property is read.
+
+ Returns any previous binding associated with the property, or a
+ default-constructed QPropertyBinding<T>.
+*/
+
+/*!
+ \fn template <typename T> QPropertyBinding<T> bool QPropertyAlias<T>::setBinding(const QUntypedPropertyBinding &newBinding)
+ \overload
+
+ Associates the value of the aliased property with the provided \a newBinding
+ expression. The first time the property value is read, either from the
+ property itself or from any alias, the binding is evaluated. Whenever a
+ dependency of the binding changes, the binding will be re-evaluated the next
+ time the value of this property is read.
+
+ Returns true if the type of this property is the same as the type the binding
+ function returns; false otherwise.
+*/
+
+/*!
+ \fn template <typename T> template <typename Functor> QPropertyBinding<T> setBinding(Functor f)
+ \overload
+
+ Associates the value of the aliased property with the provided functor \a f
+ expression. The first time the property value is read, either from the
+ property itself or from any alias, the binding is evaluated. Whenever a
+ dependency of the binding changes, the binding will be re-evaluated the next
+ time the value of this property is read.
+
+ Returns any previous binding associated with the property, or a
+ default-constructed QPropertyBinding<T>.
+*/
+
+/*!
+ \fn template <typename T> bool QPropertyAlias<T>::hasBinding() const
+
+ Returns true if the aliased property is associated with a binding; false
+ otherwise.
+*/
+
+/*!
+ \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::binding() const
+
+ Returns the binding expression that is associated with the aliased property. A
+ default constructed QPropertyBinding<T> will be returned if no such
+ association exists.
+*/
+
+/*!
+ \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::takeBinding()
+
+ Disassociates the binding expression from the aliased property and returns it.
+ After calling this function, the value of the property will only change if
+ you assign a new value to it, or when a new binding is set.
+*/
+
+/*!
+ \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::onValueChanged(Functor f)
+
+ Registers the given functor \a f as a callback that shall be called whenever
+ the value of the aliased property changes.
+
+ The callback \a f is expected to be a type that has a plain call operator () without any
+ parameters. This means that you can provide a C++ lambda expression, an std::function
+ or even a custom struct with a call operator.
+
+ The returned property change handler object keeps track of the registration. When it
+ goes out of scope, the callback is de-registered.
+*/
+
+/*!
+ \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::subscribe(Functor f)
+
+ Subscribes the given functor \a f as a callback that is called immediately and whenever
+ the value of the aliased property changes in the future.
+
+ The callback \a f is expected to be a type that has a plain call operator () without any
+ parameters. This means that you can provide a C++ lambda expression, an std::function
+ or even a custom struct with a call operator.
+
+ The returned property change handler object keeps track of the subscription. When it
+ goes out of scope, the callback is unsubscribed.
+*/
+
+/*!
+ \fn template <typename T> bool QPropertyAlias<T>::isValid() const
+
+ Returns true if the aliased property still exists; false otherwise.
+
+ If the aliased property doesn't exist, all other method calls are ignored.
+*/
+
QT_END_NAMESPACE