Skip to content

Instantly share code, notes, and snippets.

@jewelsea
Created December 11, 2011 23:30
Show Gist options
  • Star 33 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save jewelsea/1463485 to your computer and use it in GitHub Desktop.
Save jewelsea/1463485 to your computer and use it in GitHub Desktop.
CodeMirror based code editor for JavaFX
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebView;
/**
* A syntax highlighting code editor for JavaFX created by wrapping a
* CodeMirror code editor in a WebView.
*
* See http://codemirror.net for more information on using the codemirror editor.
*/
public class CodeEditor extends StackPane {
/** a webview used to encapsulate the CodeMirror JavaScript. */
final WebView webview = new WebView();
/** a snapshot of the code to be edited kept for easy initilization and reversion of editable code. */
private String editingCode;
/**
* a template for editing code - this can be changed to any template derived from the
* supported modes at http://codemirror.net to allow syntax highlighted editing of
* a wide variety of languages.
*/
private final String editingTemplate =
"<!doctype html>" +
"<html>" +
"<head>" +
" <link rel=\"stylesheet\" href=\"http://codemirror.net/lib/codemirror.css\">" +
" <script src=\"http://codemirror.net/lib/codemirror.js\"></script>" +
" <script src=\"http://codemirror.net/mode/clike/clike.js\"></script>" +
"</head>" +
"<body>" +
"<form><textarea id=\"code\" name=\"code\">\n" +
"${code}" +
"</textarea></form>" +
"<script>" +
" var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {" +
" lineNumbers: true," +
" matchBrackets: true," +
" mode: \"text/x-java\"" +
" });" +
"</script>" +
"</body>" +
"</html>";
/** applies the editing template to the editing code to create the html+javascript source for a code editor. */
private String applyEditingTemplate() {
return editingTemplate.replace("${code}", editingCode);
}
/** sets the current code in the editor and creates an editing snapshot of the code which can be reverted to. */
public void setCode(String newCode) {
this.editingCode = newCode;
webview.getEngine().loadContent(applyEditingTemplate());
}
/** returns the current code in the editor and updates an editing snapshot of the code which can be reverted to. */
public String getCodeAndSnapshot() {
this.editingCode = (String ) webview.getEngine().executeScript("editor.getValue();");
return editingCode;
}
/** revert edits of the code to the last edit snapshot taken. */
public void revertEdits() {
setCode(editingCode);
}
/**
* Create a new code editor.
* @param editingCode the initial code to be edited in the code editor.
*/
CodeEditor(String editingCode) {
this.editingCode = editingCode;
webview.setPrefSize(650, 325);
webview.setMinSize(650, 325);
webview.getEngine().loadContent(applyEditingTemplate());
this.getChildren().add(webview);
}
}
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBoxBuilder;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.stage.Stage;
/**
* An example application which demonstrates use of a
* CodeMirror based JavaScript CodeEditor wrapped in
* a JavaFX WebView.
*/
public class CodeEditorExample extends Application {
// some sample code to be edited.
static final private String editingCode =
"import javafx.application.Application;\n" +
"import javafx.scene.Scene;\n" +
"import javafx.scene.web.WebView;\n" +
"import javafx.stage.Stage;\n" +
"\n" +
"/** Sample code editing application wrapping an editor in a WebView. */\n" +
"public class CodeEditorExample extends Application {\n" +
" public static void main(String[] args) { launch(args); }\n" +
" @Override public void start(Stage stage) throws Exception {\n" +
" WebView webView = new WebView();\n" +
" webView.getEngine().load(\"http://codemirror.net/mode/groovy/index.html\");\n" +
" final Scene scene = new Scene(webView);\n" +
" webView.prefWidthProperty().bind(scene.widthProperty());\n" +
" webView.prefHeightProperty().bind(scene.heightProperty());\n" +
" stage.setScene(scene);\n" +
" stage.show();\n" +
" }\n" +
"}";
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) throws Exception {
// create the editing controls.
Label title = new Label("Editing: CodeEditor.java");
title.setStyle("-fx-font-size: 20;");
final Label labeledCode = new Label(editingCode);
final CodeEditor editor = new CodeEditor(editingCode);
final Button revertEdits = new Button("Revert edits");
revertEdits.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent actionEvent) {
editor.revertEdits();
}
});
final Button copyCode = new Button(
"Take a snapshot from the editor and set a revert point"
);
copyCode.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent actionEvent) {
labeledCode.setText(editor.getCodeAndSnapshot());
System.out.println(editor.getCodeAndSnapshot());
}
});
// layout the scene.
final VBox layout = VBoxBuilder.create().spacing(10).children(
title,
editor,
HBoxBuilder.create().spacing(10).children(copyCode, revertEdits).build(),
labeledCode
).build();
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
// display the scene.
final Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
}
Copy link

ghost commented Dec 19, 2011

This is great, looks like all thats needed now is the solution to the following issue and we're rockin!

Runtime RT-18148
JavaScript to Java method calls in Web component [# 752342]

@PatMartin
Copy link

Awesome tutorial. This was a good start for replacing a simple TextArea with some IDE style editing capabilities. You would figure that you could inject external content back into the loaded content via:

webview.getEngine().executeScript("editor.setValue('" + myCode + "');");

But this only confused webview so I had to inject into the original template much the same as your example and load the content from scratch. Thanks for saving me a lot of time! I hadn't thought about calling executeScript and hooking the communication layer between JavaFX and WebView/JavaScript via getters and setters before. Very nice!

@psiska
Copy link

psiska commented Jul 16, 2012

From JS -> JFX

import netscape.javascript.JSObject;

JSObject win = (JSObject)webEngine.executeScript("window");
win.setMember("app", new JavaApp());

// JavaScript interface object
    private class JavaApp {
        public void exit() {
            Platform.exit();
        }
    }

and in webview

<a href="about:blank" onclick="app.exit()">Press me</a>

More can be found in following article http://docs.oracle.com/javafx/2/webview/jfxpub-webview.htm

@jewelsea
Copy link
Author

Thanks for your post sisken. The JSObject approach is an alternate mechanism for communication between Java and JavaScript where you want the JavaScript to call back into a Java class. I believe, that particular functionality was added to JavaFX <-> WebView communication after I wrote the above example. I still find the code example I provide here useful as it demonstrates a simple mechanism to trigger the communication events from Java code to make use of some javascript functions, such as editor.getValue(), which are already provided in the codeeditor library. If you needed to have triggers invoked in the other direction (e.g. from JavaScript actions which would callback into Java classes), then the JSObject callback style you have outlined would be a good way to go.

@reshav401
Copy link

wow really this is good tutorial for me. Thank you! but i have one question to ask about offline link.
How can i give path for "http://codemirror.net/lib/codemirror.js" into my project folder. I have copied all the code copy paste but not working.
Can you please suggest me some offline link to work project smoothly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment