Thursday, April 3, 2014

Poor mans live editor...

Aloha,

I was looking for an idea on how to show what one could do with the JavaFX WebView except showing html content and had an idea on how to combine the WebView and Nashorn for some live editing.
So you might have seen live editors where you can edit code and directly see some visual feedback (e.g. SwingPad).
Well I was not after recreating this editors but only on how to do this in a very simple way and this is what I've came up with.
Create a simple HTML page with just a textarea like follows:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        var doc = document;
        function getCode() {
            window.status = doc.getElementById('codeArea').value;
        }
    </script>
    <style type="text/css">
        textarea {
            font-size: 12px;
        }
    </style>
</head>
<body style='background-color: rgb(216, 222, 227)'>
<textarea id='codeArea' name='codeArea' onkeyup='getCode()' rows='23' cols='55'>
// Import JavaFX classes
var Color   = javafx.scene.paint.Color
var Led     = Packages.eu.hansolo.enzo.led.Led
var LedType = Packages.eu.hansolo.enzo.led.Led.LedType


// JavaFX Code in JavaScript syntax
led.ledType  = LedType.ROUND
led.ledColor = Color.RED
led.on       = false
</textarea>
</body>

</html>

As you can see the HTML page only has one textarea that contains some code. The code is JavaScript and should set some parameters of an Enzo Led JavaFX control later on.
I hooked up an onkeyup listener that will trigger the getCode() method each time a key was pressed.
This method only set the window.status to the current value of the textarea tag.
The nice thing in the JavaFX WebView is the possibility to hook up a ChangeListener to the window.status of the loaded HTML page.
That means as soon as the window.status in the loaded HTML page changed, the ChangeListener in the JavaFX application will be triggered and one could read the new status of the HTML page (which is the text in the textarea in this case). 
This is the point where Nashorn joins the game, I simply pass the text from the textarea to the Nashorn JavaScript engine and try to evaluate it as code. If the code is valid it will be executed immediately. To see the changes in the JavaFX application I've added the Enzo JavaFX Led control to the Scene. The last thing we need to do is to inject the Led control instance to the Nashorn scripting engine so that Nashorn is able to change the parameters of the Led control.

Here is the JavaFX code that demonstrates the live editor.

public class Demo extends Application {
    private ScriptEngineManager scriptEngineManager;
    private ScriptEngine        scriptEngine;
    private StackPane           ledContainer;
    private Led                 led;
    private WebView             webView;
    private WebEngine           webEngine;


    // ******************** Initialization ************************************
    @Override public void init() {
        led = new Led();

        ledContainer = new StackPane(led);
        ledContainer.setPrefSize(400, 400);
        ledContainer.setPadding(new Insets(10, 10, 10, 10));        

        initNashorn();
    }

    private void initNashorn() {
        scriptEngineManager = new ScriptEngineManager();
        scriptEngine        = scriptEngineManager.getEngineByName("nashorn");

        // Inject led object into Nashorn scripting engine
        scriptEngine.put("led", led);
    }

    private void initWebView() {
        webView = new WebView();
        webView.setPrefSize(600, 400);
        webEngine = webView.getEngine();
        webEngine.load(Demo.class.getResource("editor.html").toExternalForm());
        webEngine.getLoadWorker().stateProperty().addListener((ov, o, n) -> {
            if (Worker.State.SUCCEEDED == n) {
                webEngine.setOnStatusChanged(webEvent -> {
                    try {
                        scriptEngine.eval(webEvent.getData());
                    } catch (Exception e) {/* code was not valid */}
                });
            }
        });
    }


    // ******************** Application start *********************************
    @Override public void start(Stage stage) {
        initWebView();

        HBox pane = new HBox();
        pane.setSpacing(10);
        pane.getChildren().addAll(ledContainer, webView);

        Scene scene = new Scene(pane);

        stage.setTitle("WebView live edit");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

Keep in mind that you JDK8 and Enzo to run that code. The result should look similar to this...




This is a very easy poor man live editor that has a lot potential for improvements (like adding a syntax highlighting JavaScript library like highlight.js or codemirror) but it shows the power of the JavaFX WebView in combination with Nashorn.

That's it again...so enjoy Java and keep coding...

2 comments:

  1. Hi Gerrit,

    what is the reason for using WebView with a <textarea> instead of just javafx.scene.control.TextArea?

    Tomas

    ReplyDelete
    Replies
    1. Hi Tomas,
      There's no specific reason, I just would like to show another usage of the webview. Would be nice to use your JavaFX editor with Syntax highlighting instead :)
      Cheers,
      Gerrit

      Delete