如何将 Tinymce 集成到 JavaFx webview 中?

How to integrate Tinymce into JavaFx webview?

我正在尝试在我们的 JavaFX 桌面应用程序中添加 Tinymce,因此我需要在 Tinymce 和 FX webview 之间进行双向通信。

到目前为止我做了什么: 在javaFX webview中集成Tinymce并显示,但是不知道Tinymce和javaFx webview之间如何通信?

谁有经验或者能回答一下?

这里有实现源码

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 包含 Tinymce 和 javascript 代码的页面:

<!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>

最后我让它按下面的方式工作,可能对其他人有帮助。任何好的建议或更好的解决方案将不胜感激..

  1. 在脚本中创建全局var tinyEditor;

  2. 在设置函数开始时启动它:tinyEditor = editor;

  3. 在 javaFx 中创建对此编辑器的引用

    // JavaScript interface object
    public class JavaFxApp {
        private JSObject tinyEditor;
    
        public void init(JSObject tinyEditor) {
            this.tinyEditor = tinyEditor;
            }
     }
    
  4. 使用 webEngin 启动对 tinymceEditor 的引用,如下所示。

         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);" );
            }
        });
    
  5. 设置内容:javaFxApp.tinyEditor.call("setContent", "Your content here");

  6. 通过以下方式获取内容:String content = (String) javaFxApp.tinyEditor.call("getContent");