I am trying to add Tinymce in our JavaFX desktop application therefore I need a bidirectional communication between Tinymce and FX webview.
What I have done up to now: Integrated Tinymce in javaFX webview and displaying it, but don't know how to communicate between Tinymce and javaFx webview?
Anyone has experience or can answer the following?
Here are realization source code.
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Border;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.events.EventTarget;
import static javafx.concurrent.Worker.State;
public class TinymceInJavaFx extends Application {
private static String URL_TINYMCE_PAGE = "/html/tinymcePage.html";
private static String ID = "tinymceTextarea";
private static String INITIAL_TEXT = "Initial text to be loaded in Tinymce.";
private WebEngine webEngine;
private Document document;
private Element element;
final TextArea fxTextArea = new TextArea(INITIAL_TEXT);
public JavaFxApp javaFxApp;
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX HTML Editor Tinymce");
WebView webView = new WebView();
webEngine = webView.getEngine();
webEngine.load(getClass().getResource(URL_TINYMCE_PAGE).toExternalForm());
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
public void changed(ObservableValue<? extends State> ov, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
primaryStage.setTitle(webEngine.getLocation());
initJavaAndJavascriptObject();
element.setTextContent(fxTextArea.getText());
}
}
});
VBox vBox = new VBox(webView);
vBox.setStyle("-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: blue;");
fxTextArea.setBorder(Border.EMPTY);
fxTextArea.setStyle("-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: red;");
Button btnSave = new Button("Update FX TextArea");
btnSave.setOnAction((ActionEvent t) -> {
fxTextArea.setText(element.getTextContent());
});
// create the toolbar
VBox toolBar = new VBox();
toolBar.setMinHeight(200.0);
toolBar.setAlignment(Pos.CENTER);
toolBar.getStyleClass().add("browser-toolbar");
toolBar.getChildren().addAll(btnSave, fxTextArea);
Scene scene = new Scene(new VBox(vBox, toolBar), 1000, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void initJavaAndJavascriptObject() {
document = webEngine.getDocument();
element = document.getElementById(ID);
addChangeListenerToTinymceEditorTextareaElement(element);
//Mapping Java objects to JavaScript values, Calling back to Java from JavaScript
JSObject window = (JSObject) webEngine.executeScript("window");
// You can then refer to the object and the method from your HTML page: <a href="" onclick="fxApp.update()">Click here to exit application</a>
window.setMember("fxApp", javaFxApp);
}
// JavaScript interface object
public class JavaFxApp {
public JavaFxApp() {
}
public void update(String content) {
fxTextArea.setText(content);
}
public void reset() {
fxTextArea.setText("");
}
}
private void addChangeListenerToTinymceEditorTextareaElement(Element el) {
((EventTarget) el).addEventListener(
"onchange",
event -> fxTextArea.setText("Added by textarea change listener. ".concat(el.getTextContent())),
false
);
}
}
html page which contains Tinymce and javascript codes:
<!DOCTYPE html>
<html>
<head>
<script src="../js/tinymce4/tinymce.min.js"></script>
<script type="text/javascript">
tinymce.init({
selector: "#tinymceTextarea",
width: '100%',
height: 800,
placeholder:'Typ of plak hier...',
plugins: [
"advlist autolink lists link image charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table paste",
],
toolbar:
"undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | code openlocalfile",
setup: function(editor) {
function displayFile(contents) {
editor.insertContent(contents);
}
function openLocalFile() {
const readFile = function (e) {
let file = e.target.files[0];
if (!file) {
return;
}
let reader = new FileReader();
reader.onload = function (e) {
let contents = e.target.result;
fileInput.func(contents)
document.body.removeChild(fileInput)
}
reader.readAsText(file)
};
let fileInput = document.createElement("input")
fileInput.type='file'
fileInput.style.display='none'
fileInput.onchange=readFile
fileInput.func=displayFile
document.body.appendChild(fileInput)
fileInput.dispatchEvent(new MouseEvent('click', {shiftKey: true}));
}
editor.addButton('openlocalfile', {
text:"Open file",
icon: 'browse',
//image: 'https://cdn-icons.flaticon.com/png/512/5994/premium/5994754.png?token=exp=1643109307~hmac=6e7bf649b369f4936adba174205d9e5c',
tooltip: "Open a local File in the editor",
onclick: openLocalFile
});
}
});
function callJavaFx(){
return tinymce.getContent();
}
</script>
</head>
<body>
<div>Tinymce <b>locally</b> hosted and <strong>Opensource</strong><br>
<input type="button" onclick="fxApp.reset()" value="Reset FX Textarea from html page">
<input type="button" onclick="fxApp.update(callJavaFx());" value="Save tinymce content to FX textarea">
<hr>
</div>
<form method="post">
<textarea id="tinymceTextarea" onchange="fxApp.update()" >I'm initial text value in opensource self-hosted HTML page.</textarea>
</form>
</body>
</html>
Finally I get it to work as below, may it help someone else. Any nice suggestion or better solution will be appreciated..
Create a global var tinyEditor;
in the script.
Initiate it at the begin of setup function: tinyEditor = editor;
Create the reference to this editor in javaFx
// JavaScript interface object
public class JavaFxApp {
private JSObject tinyEditor;
public void init(JSObject tinyEditor) {
this.tinyEditor = tinyEditor;
}
}
Initiate reference to tinymceEditor with webEngin as below.
webEngine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
if (newState == State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("fxApp", javaFxApp = new JavaFxApp());
webEngine.executeScript( "window.fxApp.init(window.tinyEditor);" );
}
});
Set content by : javaFxApp.tinyEditor.call("setContent", "Your content here");
Get content by : String content = (String) javaFxApp.tinyEditor.call("getContent");