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 65ca19a  ISIS-2340: fx: bind validation feedback to UI comp. (initial)
65ca19a is described below

commit 65ca19ad1f92f2906473136a9373d39b3a184e6b
Author: Andi Huber <[email protected]>
AuthorDate: Sun Aug 16 19:01:21 2020 +0200

    ISIS-2340: fx: bind validation feedback to UI comp. (initial)
---
 .../viewer/javafx/model/binding/BindingsFx.java    | 22 ++++++++
 .../incubator/viewer/javafx/model/util/_fx.java    | 61 ++++++++++++++++------
 .../ui/components/number/NumberFieldFactory.java   | 20 +++++--
 3 files changed, 81 insertions(+), 22 deletions(-)

diff --git 
a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java
 
b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java
index debd155..9e28653 100644
--- 
a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java
+++ 
b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java
@@ -29,6 +29,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.viewer.common.model.binding.BindingConverter;
 
 import javafx.beans.property.Property;
+import javafx.beans.property.StringProperty;
 import javafx.beans.value.ObservableValue;
 import lombok.NonNull;
 import lombok.val;
@@ -37,6 +38,16 @@ import lombok.experimental.UtilityClass;
 @UtilityClass
 public class BindingsFx {
     
+    public static <T> void bind(
+            final @NonNull Property<T> leftProperty, 
+            final @NonNull Observable<T> rightObservable) {
+
+        leftProperty.setValue(rightObservable.getValue());
+        rightObservable.addListener((e,o,n)->{
+            leftProperty.setValue(n);
+        });
+    }
+    
     public static <L> void bind(
             final @NonNull Property<L> leftProperty, 
             final @NonNull Observable<ManagedObject> rightObservable, 
@@ -58,6 +69,17 @@ public class BindingsFx {
         rightProperty.addListener(binding);
     }
     
+    // -- VALIDATION
+    
+    public static void bindValidationFeeback(
+            final @NonNull StringProperty textProperty,
+            final @NonNull Property<Boolean> visibilityProperty,
+            final @NonNull Observable<String> textObservable) {
+
+        bind(textProperty, textObservable);
+        visibilityProperty.bind(textProperty.isNotEmpty());
+    }
+    
     // -- INTERNAL
     
     private static class InternalBidirBinding<T> 
diff --git 
a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java
 
b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java
index 0106404..31ac904 100644
--- 
a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java
+++ 
b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java
@@ -22,10 +22,6 @@ import java.util.function.Predicate;
 
 import javax.annotation.Nullable;
 
-import lombok.NonNull;
-import lombok.val;
-import lombok.experimental.UtilityClass;
-
 import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.property.ReadOnlyStringWrapper;
 import javafx.beans.value.ObservableValue;
@@ -65,6 +61,9 @@ import javafx.scene.layout.VBox;
 import javafx.scene.paint.Color;
 import javafx.scene.text.Font;
 import javafx.scene.text.FontWeight;
+import lombok.NonNull;
+import lombok.val;
+import lombok.experimental.UtilityClass;
 
 @UtilityClass
 public final class _fx {
@@ -91,6 +90,21 @@ public final class _fx {
         container.getChildren().add(component);
         return component;
     }
+    
+    public static Label newValidationFeedback(Pane container) {
+        val component = new Label();
+        container.getChildren().add(component);
+        visibilityLayoutFix(component);
+        component.setStyle("-fx-color: red");
+        component.visibleProperty().addListener((e, o, visible)->{
+            if(visible) {
+                borderDashed(container, Color.RED);
+            } else {
+                container.setBorder(null);
+            }
+        });
+        return component;
+    }
 
     public static Button newButton(Pane container, String label, 
EventHandler<ActionEvent> eventHandler) {
         val component = new Button(label);
@@ -200,7 +214,19 @@ public final class _fx {
         tableView.getColumns().add(column);
         return column;
     }
+    
+    // -- VISIBILITY
 
+    /**
+     * Do not consider node for layout calculations, when not visible
+     * @param node
+     * @return
+     */
+    public static Node visibilityLayoutFix(Node node) {
+        node.managedProperty().bind(node.visibleProperty());
+        return node;
+    }
+    
     // -- ICONS
 
     public static Image imageFromClassPath(@NonNull Class<?> cls, String 
resourceName) {
@@ -258,19 +284,20 @@ public final class _fx {
         }
     }
 
-    private static boolean isEmptyOrHidden(Node component) {
-        if(component instanceof Pane) {
-            val children = ((Pane) component).getChildrenUnmodifiable();
-            if(children.isEmpty()) {
-                return true; // empty
-            }
-            //return true only if all children are empty or hidden    
-            val atLeastOneIsNonEmptyOrVisible = children.stream()
-                    .anyMatch(child->!isEmptyOrHidden(child));
-            return !atLeastOneIsNonEmptyOrVisible;
-        } 
-        return !component.isVisible();
-    }
+//XXX Superseded by visibilityLayoutFix(Node node)    
+//    private static boolean isEmptyOrHidden(Node component) {
+//        if(component instanceof Pane) {
+//            val children = ((Pane) component).getChildrenUnmodifiable();
+//            if(children.isEmpty()) {
+//                return true; // empty
+//            }
+//            //return true only if all children are empty or hidden    
+//            val atLeastOneIsNonEmptyOrVisible = children.stream()
+//                    .anyMatch(child->!isEmptyOrHidden(child));
+//            return !atLeastOneIsNonEmptyOrVisible;
+//        } 
+//        return !component.isVisible();
+//    }
 
 
     public static void hideUntilPopulated(Pane component) {
diff --git 
a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
 
b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
index 0b64556..890e6f2 100644
--- 
a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
+++ 
b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
@@ -26,12 +26,14 @@ import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedParameter;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty;
 import org.apache.isis.incubator.viewer.javafx.model.binding.BindingsFx;
+import org.apache.isis.incubator.viewer.javafx.model.util._fx;
 import 
org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentHandlerFx;
 import 
org.apache.isis.viewer.common.model.binding.NumberConverterForStringComponent;
 import 
org.apache.isis.viewer.common.model.components.UiComponentFactory.ComponentRequest;
 
 import javafx.scene.Node;
 import javafx.scene.control.TextField;
+import javafx.scene.layout.VBox;
 import lombok.RequiredArgsConstructor;
 import lombok.val;
 
@@ -48,7 +50,9 @@ public class NumberFieldFactory implements 
UiComponentHandlerFx {
     @Override
     public Node handle(ComponentRequest request) {
 
-        val uiComponent = new TextField();
+        val uiComponent = new VBox();
+        val uiField = _fx.add(uiComponent, new TextField());
+        val uiValidationFeedback = _fx.newValidationFeedback(uiComponent);
         val valueSpec = request.getFeatureTypeSpec();
         val converter = new NumberConverterForStringComponent(valueSpec);
         
@@ -57,11 +61,14 @@ public class NumberFieldFactory implements 
UiComponentHandlerFx {
             val managedParameter = 
(ManagedParameter)request.getManagedFeature();
 
             BindingsFx.bindBidirectional(
-                    uiComponent.textProperty(),
+                    uiField.textProperty(),
                     managedParameter.getValue(),
                     converter);
-
-            //TODO bind parameter validation feedback
+            
+            BindingsFx.bindValidationFeeback(
+                    uiValidationFeedback.textProperty(),
+                    uiValidationFeedback.visibleProperty(),
+                    managedParameter.getValidationMessage());
 
         } else if(request.getManagedFeature() instanceof ManagedProperty) {
 
@@ -69,12 +76,15 @@ public class NumberFieldFactory implements 
UiComponentHandlerFx {
 
             // readonly binding
             BindingsFx.bind(
-                    uiComponent.textProperty(),
+                    uiField.textProperty(),
                     managedProperty.getValue(),
                     converter);
 
             //TODO allow property editing
+            uiField.editableProperty().set(false);
+            
             //TODO bind property validation feedback
+
         }
 
         return uiComponent;

Reply via email to