io.qt.core.QProperty Maven / Gradle / Ivy
/****************************************************************************
**
** Copyright (C) 2009-2024 Dr. Peter Droste, Omix Visualization GmbH & Co. KG. All rights reserved.
**
** This file is part of Qt Jambi.
**
** ** $BEGIN_LICENSE$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
** $END_LICENSE$
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
package io.qt.core;
import java.util.Objects;
import io.qt.*;
/**
* Java wrapper for Qt class QProperty
* The QProperty class enables automatic property bindings.
* Example:
*
* QIntProperty red = new QIntProperty(255);
* QIntProperty green = new QIntProperty(0);
* QIntProperty blue = new QIntProperty(0);
* QIntProperty alpha = new QIntProperty(255);
* QProperty<QColor> color = new QProperty<>(QColor.class);
* color.setBinding(()->new QColor(red.value(), green.value(), blue.value(), alpha.value()));
* color.value(); // = red
* red.setValue(0);
* green.setValue(255);
* color.value(); // = green
*
* For primitive-typed implementations see:
*
* - {@link QBooleanProperty}
* - {@link QByteProperty}
* - {@link QShortProperty}
* - {@link QIntProperty}
* - {@link QLongProperty}
* - {@link QFloatProperty}
* - {@link QDoubleProperty}
* - {@link QCharProperty}
*
*/
public final class QProperty extends QPropertyData {
private static class TypedPropertyBindingData extends QPropertyBindingData{
TypedPropertyBindingData(QMetaType metaType) {
super();
this.metaType = metaType;
}
@QtUninvokable
public boolean setValueBypassingBindings(QProperty> property, Object val) {
return QProperty.setValueBypassingBindings(QtJambi_LibraryUtilities.internal.nativeId(property), QtJambi_LibraryUtilities.internal.checkedNativeId(this.metaType), val);
}
final QMetaType metaType;
}
private static class RCTypedPropertyBindingData extends TypedPropertyBindingData{
RCTypedPropertyBindingData(QMetaType metaType, Object val) {
super(metaType);
__rcValue = val;
}
@Override
public boolean setValueBypassingBindings(QProperty> property, Object val) {
if(super.setValueBypassingBindings(property, val)) {
__rcValue = val;
return true;
}
else return false;
}
@SuppressWarnings("unused")
private Object __rcValue;
}
private static QMetaType valueMetaType(Object val) {
if(val instanceof QList) {
return QMetaType.fromType(QList.class, ((QList>) val).elementMetaType());
}else if(val instanceof QSet) {
return QMetaType.fromType(QSet.class, ((QSet>) val).elementMetaType());
}else if(val instanceof QQueue) {
return QMetaType.fromType(QQueue.class, ((QQueue>) val).elementMetaType());
}else if(val instanceof QStack) {
return QMetaType.fromType(QStack.class, ((QStack>) val).elementMetaType());
}else if(val instanceof QMap) {
return QMetaType.fromType(QMap.class, ((QMap,?>) val).keyMetaType(), ((QMap,?>) val).valueMetaType());
}else if(val instanceof QHash) {
return QMetaType.fromType(QHash.class, ((QHash,?>) val).keyMetaType(), ((QHash,?>) val).valueMetaType());
}else if(val instanceof QMultiMap) {
return QMetaType.fromType(QMultiMap.class, ((QMultiMap,?>) val).keyMetaType(), ((QMultiMap,?>) val).valueMetaType());
}else if(val instanceof QMultiHash) {
return QMetaType.fromType(QMultiHash.class, ((QMultiHash,?>) val).keyMetaType(), ((QMultiHash,?>) val).valueMetaType());
}else if(val instanceof QPair) {
return QMetaType.fromType(QPair.class, valueMetaType(((QPair,?>) val).first), valueMetaType(((QPair,?>) val).second));
}else if(val instanceof Integer) {
return new QMetaType(QMetaType.Type.Int);
}else if(val instanceof Long) {
return new QMetaType(QMetaType.Type.LongLong);
}else if(val instanceof Short) {
return new QMetaType(QMetaType.Type.Short);
}else if(val instanceof Byte) {
return new QMetaType(QMetaType.Type.SChar);
}else if(val instanceof Double) {
return new QMetaType(QMetaType.Type.Double);
}else if(val instanceof Float) {
return new QMetaType(QMetaType.Type.Float);
}else if(val instanceof Character) {
return new QMetaType(QMetaType.Type.QChar);
}else if(val instanceof Boolean) {
return new QMetaType(QMetaType.Type.Bool);
}else if(val instanceof QNativePointer) {
return new QMetaType(QMetaType.Type.VoidStar);
}else {
return QMetaType.fromType(QtJambi_LibraryUtilities.internal.getClass(val));
}
}
/**
* Constructs a property with the given type and initialValue.
* @param initialValue
* @param type class type
* @param instantiations optional instantiations for container classes like {@link QList} and {@link QMap}
*/
public QProperty(T initialValue, @StrictNonNull Class type, @StrictNonNull QMetaType @StrictNonNull... instantiations) {
super(null);
QMetaType metaType = QMetaType.fromType(type, instantiations);
if(metaType.flags().testFlag(QMetaType.TypeFlag.IsPointer) || metaType.name().contains("*")) {
d = new RCTypedPropertyBindingData(metaType, initialValue);
}else {
d = new TypedPropertyBindingData(metaType);
}
initialize_native(this, metaType, initialValue);
}
/**
* Constructs a property with the given type.
* @param type class type
* @param instantiations optional instantiations for container classes like {@link QList} and {@link QMap}
*/
public QProperty(@StrictNonNull Class type, @StrictNonNull QMetaType @StrictNonNull... instantiations) {
this((T)null, type, instantiations);
}
/**
* Constructs a property with the provided initialValue. The property type is extracted from initialValue
.
* @param initialValue initial value must not be null
*/
public QProperty(@StrictNonNull T initialValue) {
super(null);
QMetaType metaType = valueMetaType(Objects.requireNonNull(initialValue, "Argument 'initialValue': null not expected."));
if(metaType.flags().testFlag(QMetaType.TypeFlag.IsPointer) || metaType.name().contains("*")) {
d = new RCTypedPropertyBindingData(metaType, initialValue);
}else {
d = new TypedPropertyBindingData(metaType);
}
initialize_native(this, metaType, initialValue);
}
/**
* Constructs a QVariant
-typed property.
*/
public QProperty() {
super(null);
QMetaType metaType = new QMetaType(QMetaType.Type.QVariant);
d = new TypedPropertyBindingData(metaType);
initialize_native(this, metaType, null);
}
/**
* Constructs a property with the provided binding. The property type corresponds to the type of binding
.
* @param binding must not be null
*/
public QProperty(@StrictNonNull QPropertyBinding binding) {
super(null);
QMetaType metaType = binding.valueMetaType();
if(metaType.flags().testFlag(QMetaType.TypeFlag.IsPointer) || metaType.name().contains("*")) {
d = new RCTypedPropertyBindingData(metaType, null);
}else {
d = new TypedPropertyBindingData(metaType);
}
initialize_native(this, metaType, null);
d.setBinding(binding, this);
}
/**
* Constructs a property bound to the provided functor
.
* The property type corresponds to the return type of the functor
's {@link java.util.function.Supplier#get()}.
* @param functor
*/
public QProperty(QtUtilities.@StrictNonNull Supplier functor) {
this(new QPropertyBinding<>(functor));
}
/**
* Constructs a property bound to the provided functor
.
* The property type corresponds to the return type of the functor
's {@link java.util.function.Supplier#get()}.
* @param functor
* @param type class type
* @param instantiations optional instantiations for container classes like {@link QList} and {@link QMap}
*/
public QProperty(QtUtilities.@StrictNonNull Supplier functor, @StrictNonNull Class type, @StrictNonNull QMetaType @StrictNonNull... instantiations) {
super(null);
QMetaType metaType = QMetaType.fromType(type, instantiations);
if(metaType.flags().testFlag(QMetaType.TypeFlag.IsPointer) || metaType.name().contains("*")) {
d = new RCTypedPropertyBindingData(metaType, null);
}else {
d = new TypedPropertyBindingData(metaType);
}
initialize_native(this, metaType, null);
if(functor!=null)
setBinding(functor);
}
/**
* Returns the value of the property. This may evaluate a binding expression that is tied to this property, before returning the value.
* @return value
*/
@QtUninvokable
public T value()
{
if (d.hasBinding())
d.evaluateIfDirty(this);
d.registerWithCurrentlyEvaluatingBinding();
return this.getValueBypassingBindings();
}
/**
* Assigns newValue to this property and removes the property's associated binding, if present.
* @param newValue
*/
@QtUninvokable
public void setValue(T newValue)
{
d.removeBinding();
if (d.setValueBypassingBindings(this, newValue))
d.notifyObservers(this);
}
@QtUninvokable
static boolean checkType(QMetaType myMetaType, QMetaType metaType) {
if(myMetaType.sizeOf()==1 // byte
&& (myMetaType.id()==QMetaType.Type.Char.value()
|| myMetaType.id()==QMetaType.Type.SChar.value()
|| myMetaType.id()==QMetaType.Type.UChar.value())) {
if(metaType.sizeOf()!=1
|| (metaType.id()!=QMetaType.Type.SChar.value()
&& metaType.id()!=QMetaType.Type.Char.value()
&& metaType.id()!=QMetaType.Type.UChar.value())) {
return false;
}
} else if(myMetaType.sizeOf()==2 // char
&& (myMetaType.id()==QMetaType.Type.QChar.value()
|| myMetaType.id()==QMetaType.Type.Char16.value()
|| myMetaType.id()==QMetaType.Type.Short.value()
|| myMetaType.id()==QMetaType.Type.UShort.value())) {
if(metaType.sizeOf()!=2
|| (metaType.id()!=QMetaType.Type.QChar.value()
&& metaType.id()!=QMetaType.Type.Char16.value()
&& metaType.id()!=QMetaType.Type.Short.value()
&& metaType.id()!=QMetaType.Type.UShort.value())) {
return false;
}
} else if(myMetaType.sizeOf()==4 // int
&& (myMetaType.id()==QMetaType.Type.Int.value()
|| myMetaType.id()==QMetaType.Type.Long.value()
|| myMetaType.id()==QMetaType.Type.UInt.value()
|| myMetaType.id()==QMetaType.Type.ULong.value()
|| myMetaType.id()==QMetaType.Type.Char32.value())) {
if(metaType.sizeOf()!=4
|| (metaType.id()!=QMetaType.Type.Int.value()
&& metaType.id()!=QMetaType.Type.Long.value()
&& metaType.id()!=QMetaType.Type.ULong.value()
&& metaType.id()!=QMetaType.Type.UInt.value()
&& metaType.id()!=QMetaType.Type.Char32.value())) {
return false;
}
} else if(myMetaType.sizeOf()==8 // long
&& (myMetaType.id()==QMetaType.Type.LongLong.value()
|| myMetaType.id()==QMetaType.Type.Long.value()
|| myMetaType.id()==QMetaType.Type.ULongLong.value()
|| myMetaType.id()==QMetaType.Type.ULong.value())) {
if(metaType.sizeOf()!=8
|| (metaType.id()!=QMetaType.Type.LongLong.value()
&& metaType.id()!=QMetaType.Type.Long.value()
&& metaType.id()!=QMetaType.Type.ULong.value()
&& metaType.id()!=QMetaType.Type.ULongLong.value())) {
return false;
}
} else if(myMetaType.sizeOf()==4 // float
&& (myMetaType.id()==QMetaType.Type.Double.value()
|| myMetaType.id()==QMetaType.Type.Float.value())) {
if(metaType.sizeOf()!=4
|| (metaType.id()!=QMetaType.Type.Double.value()
&& metaType.id()!=QMetaType.Type.Float.value())) {
return false;
}
} else if(myMetaType.sizeOf()==8 // double
&& (myMetaType.id()==QMetaType.Type.Double.value()
|| myMetaType.id()==QMetaType.Type.Float.value())) {
if(metaType.sizeOf()!=8
|| (metaType.id()!=QMetaType.Type.Double.value()
&& metaType.id()!=QMetaType.Type.Float.value())) {
return false;
}
} else if(!myMetaType.equals(metaType)) {
Class> myType = myMetaType.javaType();
if(myType==null || metaType.javaType()==null || !myType.isAssignableFrom(metaType.javaType())) {
return false;
}
}
return true;
}
/**
* Associates the value of this property with the provided newBinding
expression and returns the previously associated binding.
* The binding's value type ({@link QUntypedPropertyBinding#valueMetaType()}) has to be equals to the property's value type T
,
* otherwise the property remains unchanged.
* The first time the property value is read, 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.
* @param newBinding
* @return oldBinding
*/
@SuppressWarnings("unchecked")
@QtUninvokable
public @NonNull QPropertyBinding setBinding(@StrictNonNull QPropertyBinding newBinding)
{
if(newBinding!=null && !checkType(valueMetaType(), newBinding.valueMetaType()))
return new QPropertyBinding<>();
QPropertyBindingData bd = bindingData();
QUntypedPropertyBinding oldBinding = bd.setBinding(newBinding, this);
d.notifyObservers(this);
if(oldBinding instanceof QPropertyBinding)
return (QPropertyBinding)oldBinding;
else
return new QPropertyBinding<>(oldBinding);
}
/**
* Associates the value of this property with the provided newBinding
expression.
* The binding's value type ({@link QUntypedPropertyBinding#valueMetaType()}) has to be equals to the property's value type T
,
* otherwise the property remains unchanged and the method returns false
.
* The first time the property value is read, 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.
* @param newBinding
* @return true if types match, false otherwise.
*/
@QtUninvokable
public boolean setBinding(@StrictNonNull QUntypedPropertyBinding newBinding)
{
if(newBinding!=null && !checkType(valueMetaType(), newBinding.valueMetaType()))
return false;
d.setBinding(newBinding, this);
d.notifyObservers(this);
return true;
}
/**
* Associates the value of this property with the provided functor
and returns the previously associated binding.
* The first time the property value is read, the binding is evaluated by invoking {@link java.util.function.Supplier#get()} of functor
.
* Whenever a dependency of the binding changes, the binding will be re-evaluated the next time the value of this property is read.
* @param functor
* @return oldBinding
*/
@QtUninvokable
public @NonNull QPropertyBinding setBinding(QtUtilities.@StrictNonNull Supplier functor)
{
try {
QPropertyBinding.setPendingMetaType(this::valueMetaType);
return setBinding(new QPropertyBinding<>(functor));
}finally {
QPropertyBinding.setPendingMetaType(null);
}
}
@QtUninvokable
private QPropertyBinding makeBinding()
{
try {
QPropertyBinding.setPendingMetaType(this::valueMetaType);
return new QPropertyBinding<>(this::value);
}finally {
QPropertyBinding.setPendingMetaType(null);
}
}
/**
* Checks if the property has a binding.
* @return true if the property has a binding, false otherwise.
*/
@QtUninvokable
public boolean hasBinding() { return d.hasBinding(); }
/**
* Returns the binding expression that is associated with this property.
* A default constructed {@link QPropertyBinding}<T> will be returned if no such association exists.
* @return binding
*/
@QtUninvokable
public @NonNull QPropertyBinding binding()
{
return new QPropertyBinding<>(this);
}
/**
* Disassociates the binding expression from this 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.
* @return the removed binding
*/
@QtUninvokable
public @NonNull QPropertyBinding takeBinding()
{
return new QPropertyBinding<>(d.setBinding(new QPropertyBinding<>(), this));
}
/**
* Registers the given functor f as a callback that shall be called whenever the value of the property changes.
* The returned property change handler object keeps track of the registration.
* As long as the change handler is alive i.e. as long as a reference to the {@link QPropertyChangeHandler} instance exists,
* the callback remains installed. When the garbage collection deletes the instance, the callback is de-registered.
* @param f
* @return property change handler
* @see QPropertyChangeHandler
*/
@QtUninvokable
public @NonNull QPropertyChangeHandler onValueChanged(@StrictNonNull Runnable f)
{
return new QPropertyChangeHandler(d, f);
}
/**
* Subscribes the given functor f as a callback that is called immediately and whenever the value of the property changes in the future.
* @param f
* @return property change handler
* @see QPropertyChangeHandler
* @see #onValueChanged(Runnable)
*/
@QtUninvokable
public @NonNull QPropertyChangeHandler subscribe(@StrictNonNull Runnable f)
{
f.run();
return onValueChanged(f);
}
/**
* Registers the given functor f as a callback that shall be called whenever the value of the bindable changes.
* The returned property notifier object keeps track of the registration.
* As long as the notifier is alive i.e. as long as a reference to the {@link QPropertyNotifier} instance exists,
* the callback remains installed. When the garbage collection deletes the instance, the callback is de-registered.
* @param f
* @return property notifier
* @see QPropertyNotifier
*/
@io.qt.QtUninvokable
public @NonNull QPropertyNotifier addNotifier(@StrictNonNull Runnable f)
{
return new QPropertyNotifier(d, f);
}
@QtUninvokable
QPropertyBindingData bindingData() { return d; }
QMetaType valueMetaType() {
return d.metaType;
}
private final TypedPropertyBindingData d;
native static void initialize_native(QPropertyData instance, QMetaType metaType, Object val);
/**
* {@inheritDoc}
*/
@QtUninvokable
@Override
public final T getValueBypassingBindings() {
return getValueBypassingBindings(QtJambi_LibraryUtilities.internal.nativeId(this), QtJambi_LibraryUtilities.internal.checkedNativeId(d.metaType));
}
@QtUninvokable
static native T getValueBypassingBindings(long data, long metaType);
/**
* {@inheritDoc}
*/
@QtUninvokable
@Override
public final boolean setValueBypassingBindings(T val) {
return d.setValueBypassingBindings(this, val);
}
@QtUninvokable
static native boolean setValueBypassingBindings(long data, long metaType, Object val);
}