Issue
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?
- How do I get content from Tinymce to javaFx component for example textarea?
- How to setup Tinymce content from JavaFX after fx application is loaded?
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>
Solution
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");
Answered By - michdraft lord
Answer Checked By - Timothy Miller (JavaFixing Admin)