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/06/29 18:57:39 UTC

[isis] branch master updated: ISIS-2340: fx: WebView with autofit, to render Markup types

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 0bf0750  ISIS-2340: fx: WebView with autofit, to render Markup types
0bf0750 is described below

commit 0bf0750f39afc3e7f83f5086f7b03a262762f662
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jun 29 20:57:23 2020 +0200

    ISIS-2340: fx: WebView with autofit, to render Markup types
---
 .../javafx/ui/components/form/SimpleFormField.java |  41 ++++++++
 .../ui/components/markup/MarkupFieldFactory.java   | 116 ++++++++++++++++-----
 .../ui/components/other/FallbackFieldFactory.java  |  21 +---
 3 files changed, 134 insertions(+), 44 deletions(-)

diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/form/SimpleFormField.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/form/SimpleFormField.java
new file mode 100644
index 0000000..e06faef
--- /dev/null
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/form/SimpleFormField.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.incubator.viewer.javafx.ui.components.form;
+
+import org.apache.isis.applib.annotation.LabelPosition;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import javafx.scene.Node;
+
+@RequiredArgsConstructor
+public class SimpleFormField implements FormField {
+
+    @Getter(onMethod_ = {@Override})
+    protected final LabelPosition labelPosition;
+    
+    @Getter(onMethod_ = {@Override})
+    protected final Node uiLabel;
+    
+    @Getter(onMethod_ = {@Override})
+    protected final Node uiField;
+    
+
+}
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/markup/MarkupFieldFactory.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/markup/MarkupFieldFactory.java
index 7067841..32e2932 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/markup/MarkupFieldFactory.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/markup/MarkupFieldFactory.java
@@ -23,15 +23,26 @@ import org.springframework.core.annotation.Order;
 import org.apache.isis.applib.annotation.LabelPosition;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.value.Markup;
+import org.apache.isis.core.metamodel.facets.objectvalue.labelat.LabelAtFacet;
 import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentHandlerFx;
 import org.apache.isis.incubator.viewer.javafx.ui.components.form.FormField;
+import org.apache.isis.incubator.viewer.javafx.ui.components.form.SimpleFormField;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request;
 
 import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
+import javafx.application.Platform;
+import javafx.collections.ListChangeListener;
+import javafx.concurrent.Worker.State;
+import javafx.geometry.HPos;
+import javafx.geometry.VPos;
 import javafx.scene.Node;
 import javafx.scene.control.Label;
-import javafx.scene.control.TextArea;
+import javafx.scene.layout.Region;
+import javafx.scene.web.WebEngine;
+import javafx.scene.web.WebView;
+import netscape.javascript.JSException;
 
 @org.springframework.stereotype.Component
 @Order(OrderPrecedence.MIDPOINT)
@@ -49,34 +60,89 @@ public class MarkupFieldFactory implements UiComponentHandlerFx {
                 .map(Markup::asString)
                 .orElse("");
 
-//XXX Unfortunately we have no simple means of auto-fitting a WebView        
-//        val uiComponent = new WebView();
-//        uiComponent.getEngine().loadContent(markupHtml, "text/html");
-        
-        val uiComponent = new TextArea(markupHtml);
-        uiComponent.setPrefHeight(40);
+        val uiComponent = new WebViewFitContent(markupHtml);
         
         val uiLabel = new Label(request.getFeatureLabel());
         
-        return new FormField() {
-            
-            @Override
-            public Node getUiLabel() {
-                return uiLabel;
-            }
-            
-            @Override
-            public Node getUiField() {
-                return uiComponent;
-            }
-            
-            @Override
-            public LabelPosition getLabelPosition() {
-                // TODO Auto-generated method stub
-                return null;
-            }
-        };
+        val labelPosition = request.getFeatureFacet(LabelAtFacet.class)
+                .map(LabelAtFacet::label)
+                .orElse(LabelPosition.NOT_SPECIFIED);
+        
+        return new SimpleFormField(labelPosition, uiLabel, uiComponent);
     }
 
+    // -- HELPER
+    
+    /**
+     * Unfortunately we have no simple means of auto-fitting a WebView, so we need a wrapper,
+     * that executes some JavaScript on the rendered content, do determine the preferred height. 
+     * <p>
+     * @see <a href="https://stackoverflow.com/questions/25838965/size-javafx-webview-to-the-minimum-size-needed-by-the-document-body">stackoverflow</a>
+     *  
+     * @since Jun 29, 2020
+     */
+    @Log4j2
+    private static final class WebViewFitContent extends Region {
+
+        final WebView webview = new WebView();
+        final WebEngine webEngine = webview.getEngine();
+
+        public WebViewFitContent(String content) {
+            webview.setPrefHeight(5);
+
+            widthProperty().addListener((e, o, newWidth) -> {
+                    webview.setPrefWidth(newWidth.doubleValue());
+                    Platform.runLater(this::adjustHeight);
+            });
+
+            webview.getEngine().getLoadWorker().stateProperty().addListener((e, o, newState) -> {
+                if (newState == State.SUCCEEDED) {
+                    Platform.runLater(this::adjustHeight);
+                }
+            });
+
+            webview.getChildrenUnmodifiable().addListener((ListChangeListener.Change<? extends Node> change) -> {
+                val scrolls = webview.lookupAll(".scroll-bar");
+                for (val scroll : scrolls) {
+                    scroll.setVisible(false);
+                }
+            });
+
+            setContent(content);
+            getChildren().add(webview);
+        }
+
+        public void setContent(final String content) {
+            Platform.runLater(()->webEngine.loadContent(getHtml(content), "text/html"));
+            Platform.runLater(this::adjustHeight);
+        }
+
+        @Override
+        protected void layoutChildren() {
+            layoutInArea(webview, 0, 0, getWidth(), getHeight(), 0, HPos.CENTER, VPos.CENTER);
+        }
+
+        private void adjustHeight() {
+            try {
+                Object result = webEngine.executeScript(
+                        "var myDiv = document.getElementById('mydiv');" +
+                                "if (myDiv != null) myDiv.offsetHeight");
+                if (result instanceof Integer) {
+                    val height = ((Integer) result).intValue();
+                    webview.setPrefHeight(height + 20);
+                }
+            } catch (JSException e) {
+                log.error("failed to execute JavaScript to determine WebView's height", e);
+            }
+        }
+
+        private String getHtml(String content) {
+            return "<html><body>" +
+                    "<div id=\"mydiv\">" + content + "</div>" +
+                    "</body></html>";
+        }
+
+    }
+    
 
 }
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/other/FallbackFieldFactory.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/other/FallbackFieldFactory.java
index 9821d60..d43ce7d 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/other/FallbackFieldFactory.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/other/FallbackFieldFactory.java
@@ -34,12 +34,12 @@ import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentFactoryF
 import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentHandlerFx;
 import org.apache.isis.incubator.viewer.javafx.ui.components.debug.DebugField;
 import org.apache.isis.incubator.viewer.javafx.ui.components.form.FormField;
+import org.apache.isis.incubator.viewer.javafx.ui.components.form.SimpleFormField;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request;
 import org.apache.isis.viewer.common.model.debug.DebugUiModel;
 
 import lombok.val;
 
-import javafx.scene.Node;
 import javafx.scene.control.Label;
 
 @org.springframework.stereotype.Component
@@ -82,24 +82,7 @@ public class FallbackFieldFactory implements UiComponentHandlerFx {
         
         val uiLabel = new Label(request.getFeatureLabel());
         
-        return new FormField() {
-            
-            @Override
-            public Node getUiLabel() {
-                return uiLabel;
-            }
-            
-            @Override
-            public Node getUiField() {
-                return debugField;
-            }
-            
-            @Override
-            public LabelPosition getLabelPosition() {
-                return LabelPosition.TOP;
-            }
-        };
-        
+        return new SimpleFormField(LabelPosition.TOP, uiLabel, debugField);
     }
     
     private String summarize(Facet facet) {