You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/08/07 10:35:26 UTC
[isis] branch master updated: ISIS-2340: fx: bind UI props and
params to backend
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 3468a4e ISIS-2340: fx: bind UI props and params to backend
3468a4e is described below
commit 3468a4e9ec8020ed7978247e4454a935eaa15ee4
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Aug 7 12:35:07 2020 +0200
ISIS-2340: fx: bind UI props and params to backend
---
.../binding/InternalBidirectionalBinding.java | 90 ++++----
.../ui/components/text/TextFieldFactory.java | 232 +++++++++++++++++++--
2 files changed, 261 insertions(+), 61 deletions(-)
diff --git a/core/commons/src/main/java/org/apache/isis/core/commons/internal/binding/InternalBidirectionalBinding.java b/core/commons/src/main/java/org/apache/isis/core/commons/internal/binding/InternalBidirectionalBinding.java
index bdd8e17..778cc3d 100644
--- a/core/commons/src/main/java/org/apache/isis/core/commons/internal/binding/InternalBidirectionalBinding.java
+++ b/core/commons/src/main/java/org/apache/isis/core/commons/internal/binding/InternalBidirectionalBinding.java
@@ -42,11 +42,11 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
return binding;
}
- public static <T> void unbind(Bindable<T> left, Bindable<T> rigth) {
- checkParameters(left, rigth);
- val binding = new UntypedBidirectionalBinding(left, rigth);
+ public static <T> void unbind(Bindable<T> left, Bindable<T> right) {
+ checkParameters(left, right);
+ val binding = new UntypedBidirectionalBinding(left, right);
left.removeListener(binding);
- rigth.removeListener(binding);
+ right.removeListener(binding);
}
public static void unbind(Object left, Object right) {
@@ -143,49 +143,53 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
@Override
public void changed(final Observable<? extends T> observable, final T oldValue, final T newValue) {
- if (!updating) {
- final Bindable<T> left = leftRef.get();
- final Bindable<T> right = rightRef.get();
- if ((left == null) || (right == null)) {
- if (left != null) {
- left.removeListener(this);
- }
- if (right != null) {
- right.removeListener(this);
- }
+ if (updating) {
+ return;
+ }
+ val left = getLeft();
+ val right = getRight();
+
+ if ((left == null) || (right == null)) {
+ if (left != null) {
+ left.removeListener(this);
+ }
+ if (right != null) {
+ right.removeListener(this);
+ }
+ return;
+ }
+
+ try {
+ updating = true;
+ if (left == observable) {
+ right.setValue(newValue);
} else {
- try {
- updating = true;
- if (left == observable) {
- right.setValue(newValue);
- } else {
- left.setValue(newValue);
- }
- } catch (RuntimeException e) {
- try {
- if (left == observable) {
- left.setValue(oldValue);
- } else {
- right.setValue(oldValue);
- }
- } catch (Exception e2) {
- e2.addSuppressed(e);
- unbind(left, right);
- throw _Exceptions.unrecoverableFormatted(
- "Bidirectional binding failed with an attempt to restore the "
- + "Observable to the previous value. "
- + "Removing the bidirectional binding from bindables %s and %s",
- ""+left,
- ""+right,
- e2);
- }
- throw _Exceptions.unrecoverable(
- "Bidirectional binding failed, setting to the previous value", e);
- } finally {
- updating = false;
+ left.setValue(newValue);
+ }
+ } catch (RuntimeException e) {
+ try {
+ if (left == observable) {
+ left.setValue(oldValue);
+ } else {
+ right.setValue(oldValue);
}
+ } catch (Exception e2) {
+ e2.addSuppressed(e);
+ unbind(left, right);
+ throw _Exceptions.unrecoverableFormatted(
+ "Bidirectional binding failed with an attempt to restore the "
+ + "Observable to the previous value. "
+ + "Removing the bidirectional binding from bindables %s and %s",
+ ""+left,
+ ""+right,
+ e2);
}
+ throw _Exceptions.unrecoverable(
+ "Bidirectional binding failed, setting to the previous value", e);
+ } finally {
+ updating = false;
}
+
}
}
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java
index 800ddfd..50ffb4b 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java
@@ -18,20 +18,34 @@
*/
package org.apache.isis.incubator.viewer.javafx.ui.components.text;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+
import javax.inject.Inject;
import org.springframework.core.annotation.Order;
import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.core.commons.binding.Bindable;
+import org.apache.isis.core.commons.binding.ChangeListener;
+import org.apache.isis.core.commons.binding.Observable;
+import org.apache.isis.core.commons.internal.base._Casts;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
import org.apache.isis.core.metamodel.facets.value.string.StringValueFacet;
import org.apache.isis.core.metamodel.interactions.managed.ManagedParameter;
import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentHandlerFx;
import org.apache.isis.viewer.common.model.binding.UiComponentFactory.ComponentRequest;
+import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;
+import javafx.beans.property.Property;
+import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.control.TextField;
@@ -48,33 +62,215 @@ public class TextFieldFactory implements UiComponentHandlerFx {
@Override
public Node handle(ComponentRequest request) {
- //TODO 1) move all the logic that is in the request to the underlying ManagedProperty
- // 2) pass the ManagedProperty over with the request object
- // 3) design for an API to bind a ManagedProperty to a FormField, also make sure this works
- // with Vaadin's FormLayout/Field API
-// val textValue = request.getFeatureValue(String.class)
-// .orElse("");
-
val uiComponent = new TextField();
-
+ val valueSpec = request.getFeatureTypeSpec();
+ val converter = Converter.<String>of(valueSpec);
+
if(request.getManagedFeature() instanceof ManagedParameter) {
-
+
val managedParameter = (ManagedParameter)request.getManagedFeature();
-
-// uiComponent.textProperty().
-//
-// managedParameter.validate(proposedValue)
-
- //TODO bind to parameter model
-
+
+ bindBidirectional(
+ uiComponent.textProperty(),
+ managedParameter.getValue(),
+ converter);
+
+ //TODO bind parameter validation feedback
+
} else if(request.getManagedFeature() instanceof ManagedProperty) {
-
+
val managedProperty = (ManagedProperty)request.getManagedFeature();
- //TODO bind to property model
+
+ // readonly binding
+ bind(
+ uiComponent.textProperty(),
+ managedProperty.getValue(),
+ converter);
+
+ //TODO allow property editing
+ //TODO bind property validation feedback
}
return uiComponent;
}
+ // -- TODO these binding helpers need to move (for reuse) ...
+
+ @RequiredArgsConstructor(staticName = "of")
+ private static final class Converter<T> {
+
+ private final ObjectSpecification valueSpec;
+
+ public T unwrap(ManagedObject object) {
+ return _Casts.uncheckedCast(ManagedObjects.UnwrapUtil.single(object));
+ }
+
+ public ManagedObject wrap(T pojo) {
+ return ManagedObject.of(valueSpec, pojo);
+ }
+ }
+
+ private static class BidirBinding<T>
+ implements
+ javafx.beans.value.ChangeListener<T>,
+ ChangeListener<ManagedObject>{
+
+ private final WeakReference<Property<T>> leftRef;
+ private final WeakReference<Bindable<ManagedObject>> rightRef;
+ private final Converter<T> converter;
+ private boolean updating = false;
+ private final int cachedHash;
+
+ private Property<T> getLeft() {
+ return leftRef.get();
+ }
+
+ private Bindable<ManagedObject> getRight() {
+ return rightRef.get();
+ }
+
+
+ public BidirBinding(
+ final @NonNull Property<T> left,
+ final @NonNull Bindable<ManagedObject> right,
+ final @NonNull Converter<T> converter) {
+
+ this.leftRef = new WeakReference<>(left);
+ this.rightRef = new WeakReference<>(right);
+ this.converter = converter;
+ cachedHash = Objects.hash(left, right, converter);
+ }
+
+ @Override
+ public void changed(ObservableValue<? extends T> leftObservable, T oldValue, T newValue) {
+ changed(oldValue, newValue, null, null);
+ }
+
+ @Override
+ public void changed(
+ Observable<? extends ManagedObject> rightObservable,
+ ManagedObject oldValue,
+ ManagedObject newValue) {
+ changed(null, null, oldValue, newValue);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ final Object left = getLeft();
+ final Object right = getRight();
+ if ((left == null) || (right == null)) {
+ return false;
+ }
+
+ if (obj instanceof BidirBinding) {
+ final BidirBinding<?> otherBinding = (BidirBinding<?>) obj;
+ final Object otherLeft = otherBinding.getLeft();
+ final Object otherRight = otherBinding.getRight();
+ if ((otherLeft == null) || (otherRight == null)) {
+ return false;
+ }
+
+ if (left == otherLeft && right == otherRight) {
+ return true;
+ }
+ if (left == otherRight && right == otherLeft) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return cachedHash;
+ }
+
+ // -- HELPER
+
+ private void changed(T oldPojo, T newPojo, ManagedObject oldValue, ManagedObject newValue) {
+ if (updating) {
+ return;
+ }
+ val left = getLeft();
+ val right = getRight();
+ if(!isStillBound(left, right)) {
+ return;
+ }
+
+ try {
+ updating = true;
+ if(newValue!=null) {
+ left.setValue(converter.unwrap(newValue));
+ } else {
+ right.setValue(converter.wrap(newPojo));
+ }
+ } catch (RuntimeException e) {
+ try {
+ if(newValue!=null) {
+ left.setValue(converter.unwrap(oldValue));
+ } else {
+ right.setValue(converter.wrap(oldPojo));
+ }
+ } catch (Exception e2) {
+ e2.addSuppressed(e);
+ left.removeListener(this);
+ right.removeListener(this);
+ throw _Exceptions.unrecoverableFormatted(
+ "Bidirectional binding failed with an attempt to restore the "
+ + "Observable to the previous value. "
+ + "Removing the bidirectional binding from bindables %s and %s",
+ ""+left,
+ ""+right,
+ e2);
+ }
+ throw _Exceptions.unrecoverable(
+ "Bidirectional binding failed, setting to the previous value", e);
+ } finally {
+ updating = false;
+ }
+ }
+
+ private boolean isStillBound(
+ final Property<T> left,
+ final Bindable<ManagedObject> right) {
+
+ if ((left == null) || (right == null)) {
+ if (left != null) {
+ left.removeListener(this);
+ }
+ if (right != null) {
+ right.removeListener(this);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ public static <L, R> void bind(
+ final @NonNull Property<L> leftProperty,
+ final @NonNull Observable<ManagedObject> rightObservable,
+ final @NonNull Converter<L> converter) {
+
+ leftProperty.setValue(converter.unwrap(rightObservable.getValue()));
+ rightObservable.addListener((e,o,n)->{
+ leftProperty.setValue(converter.unwrap(n));
+ });
+ }
+
+ public static <L, R> void bindBidirectional(
+ final @NonNull Property<L> leftProperty,
+ final @NonNull Bindable<ManagedObject> rightProperty,
+ final @NonNull Converter<L> converter) {
+ final BidirBinding<L> binding = new BidirBinding<L>(leftProperty, rightProperty, converter);
+ leftProperty.setValue(converter.unwrap(rightProperty.getValue()));
+ leftProperty.addListener(binding);
+ rightProperty.addListener(binding);
+ }
}