2

I'm writing a custom dialog in JavaFX for my project. I need a variant but quick mode for showing the dialog content.

I know about controlsFX; actually I'm using it but I want to show the content in a WebView so it will be more controllable.

The problem is about the size of the WebView I want to find the height of the document body and set the size of the WebView to that.

I tried the following javascript hack but before showing the dialog I get 0 for the size and after showing dialog the value is not reliable. Is there a better solution or I should forget about it and try to embed a swing component that supports HTML 3?

    WebView vw = new WebView();
    vw.getEngine().loadContent("<p>This is my text</p><b>Another</b><p> This is a paragraph</p><ul><li>First cause</li><li>Second cause></li></ul>");
    vw.setPrefSize(500, 50);
    vw.setOnMouseClicked(new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent event) {
            // I see that executeScript doesn't work correctly even when the WebView is loaded
            Integer h = (Integer) vw.getEngine().executeScript("document.body.offsetHeight");
            System.out.println("scriptRES: " + h );
            vw.setPrefSize(400, h);
            vw.setMinSize(400, h);
            vw.setMaxSize(400, h);
        }
    });
James_D
  • 201,275
  • 16
  • 291
  • 322
Johnny
  • 1,509
  • 5
  • 25
  • 38

3 Answers3

2

Automated sizing support of WebView is covered by a feature request in the JavaFX issue tracker:

The feature request has currently not been implemented or scheduled for implementation. You can vote for or comment on the feature request.

There is no work-around that I know of. Earlier version of WebView seemed to work with the following code engine.executeScript("document.width") and engine.executeScript("document.height"). But these commands no longer worked last time I tried them.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
0

Until automatic preferred sizing is implemented, a certain blogmeister has come up with a solution that seems to work (for now).

It can be found in its original form on his website.

It makes having a preferred-size WebView as easy as

WebViewFitContent webViewFitContent = new WebViewFitContent(html);
yourContainer.getChildren().add(webViewFitContent);

WebViewFitContent in action


In case the link were to ever die,

WebViewFitContent.java

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.concurrent.Worker.State;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.layout.Region;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSException;

import java.util.Set;

public final class WebViewFitContent extends Region {

    final WebView webview = new WebView();
    final WebEngine webEngine = webview.getEngine();

    public WebViewFitContent(String content) {
        webview.setPrefHeight(5);

        widthProperty().addListener(new ChangeListener<Object>() {
            @Override
            public void changed(ObservableValue<?> observable, Object oldValue, Object newValue) {
                Double width = (Double)newValue;
                webview.setPrefWidth(width);
                adjustHeight();
            }
        });

        webview.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
            @Override
            public void changed(ObservableValue<? extends State> arg0, State oldState, State newState)         {
                if (newState == State.SUCCEEDED) {
                    adjustHeight();
                }
            }
        });

        webview.getChildrenUnmodifiable().addListener(new ListChangeListener<Node>() {
            @Override
            public void onChanged(ListChangeListener.Change<? extends Node> change) {
                Set<Node> scrolls = webview.lookupAll(".scroll-bar");
                for (Node scroll : scrolls) {
                    scroll.setVisible(false);
                }
            }
        });

        setContent(content);
        getChildren().add(webview);
    }

    public void setContent(final String content) {
        Platform.runLater(new Runnable(){
            @Override
            public void run() {
                webEngine.loadContent(getHtml(content));
                Platform.runLater(new Runnable(){
                    @Override
                    public void run() {
                        adjustHeight();
                    }
                });
            }
        });
    }


    @Override
    protected void layoutChildren() {
        double w = getWidth();
        double h = getHeight();
        layoutInArea(webview,0,0,w,h,0, HPos.CENTER, VPos.CENTER);
    }

    private void adjustHeight() {
        Platform.runLater(new Runnable(){
            @Override
            public void run() {
                try {
                    Object result = webEngine.executeScript(
                            "var myDiv = document.getElementById('mydiv');" +
                                    "if (myDiv != null) myDiv.offsetHeight");
                    if (result instanceof Integer) {
                        Integer i = (Integer) result;
                        double height = new Double(i);
                        height = height + 20;
                        webview.setPrefHeight(height);
                    }
                } catch (JSException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private String getHtml(String content) {
        return "<html><body>" +
                "<div id=\"mydiv\">" + content + "</div>" +
                "</body></html>";
    }

}

Note 1: In the above I added a null-check in the javascript and chose to print out any exceptions that might occur instead of ignoring them.

Note 2: Sometimes the WebView's content doesn't show through. If anyone else experiences this please leave a comment.

Brad Turek
  • 2,472
  • 3
  • 30
  • 56
  • I've tested this code with JDK8u202 and it does not seem to work. – Maxoudela May 29 '19 at 14:10
  • @Maxoudela, odd. I'm almost certain that I've used it on that JDK. I can confirm that using JDK8u211 it works as I'm using it right now. Here's the [exact code](https://gist.github.com/TurekBot/fa94bd20969dad8ad31bc5d859f7234d) I'm using. – Brad Turek May 29 '19 at 18:22
  • Sorry for the delay, thanks for the code. It seems to work in a simple case, but I'm trying to use it in a TableView/SpreadsheetView and then it does not seem to work. Also, I'm trying to make it work with the width using the same technique but apparently it's not working at all.. – Maxoudela Aug 06 '19 at 13:37
  • The webview's content missing appears to be a long standing bug... https://bugs.openjdk.java.net/browse/JDK-8087569. I've reopened it under an FX support contract in attempt to fix it... https://bugs.openjdk.java.net/browse/JDK-8237583. I've asked a similar question here: https://stackoverflow.com/questions/59803411. Meanwhile, working hard on a workaround while I'm waiting for the upstream patch. – tresf Feb 13 '20 at 22:30
-1

This is a very late answer.

You have to apply ChangeListener on the document, before calulating the height. Kindly find a running example to your question at: Correct sizing of Webview embedded in Tabelcell

However my experience is that the high is still sometimes to big.

BerndGit
  • 1,530
  • 3
  • 18
  • 47
  • This should be a comment, not an answer. If it is a duplicate question then [vote to close](http://stackoverflow.com/help/privileges/close-questions) as such and/or leave a comment once you [earn](http://meta.stackoverflow.com/q/146472) enough [reputation](http://stackoverflow.com/help/whats-reputation). If the question is not a duplicate then tailor the answer to this specific question. – Petter Friberg Mar 22 '17 at 14:42
  • I had the same question and could not find an solution here. Later I have combined the information which I have found here with a information from other question. I was getting further, but new issues poped up. So I was asking my new question, which i have linked here. So the solution to my question should also anser the question here. More details can be found at the link I have proveded. I'm not sure how to handle this conform to the SE rules. Which question shall be closed (The orginal one or my extended duplicate with active bounty and a working solution)? – BerndGit Mar 22 '17 at 15:41