This is the second day of my participation in the August More text Challenge. For details, see:August is more challenging

The premise

One day point to dig gold writing interface, discovered the built-in Markdown editor has a lot of icon, point in bytemd Markdown editor is an open source project (https://github.com/bytedance/bytemd) :

This is a NodeJs project, provided by Bytedance. Remember that there is a component in JavaFx, WebView, which already supports Html5, CSS3 and ES5. This component acts as an embedded browser. You can easily render the text content of a URL or directly render a raw Html string. Also, since native JavaFx is visually ugly, consider introducing Swing with IntelliJ IDEA themes to provide a better look. The code for this article is developed based on JDK11.

Introduction of depend on

Many people have joked that Swing components are visually poor for several reasons:

  • Technology niche, now there are better components for hybrid development and cross-platform development
  • For this reason, few people developSwingThe component’sUI, in fact,SwingCan be reimplemented for each component ofUIPerformance effect of
  • compose-jb(JetBrainsThe component was released late and happened toSwingOfficial stop maintenance, later should be less people will useSwingdoGUIThe development of

The best known solution that uses Swing and is successful is the JetBrains family bucket. So far, there are simple solutions to this “ugly” problem:

  • Option 1: Usecompose-jb(The name is a bit unpleasant, but the official warehouse ishttps://github.com/JetBrains/compose-jb) development, this isJetBrainsSeries of generic components based onSwingDo secondary encapsulation,But you have to use languageKotlinIt’s a bit of a bargainHere are two official charts for reference:

  • Scheme 2:FormDev(It was introduced beforeSwingDeveloper of layout, official websitehttps://www.formdev.com/flatlaf) provided by theFlatLaf(Flat Look and Feel), providedLight Dark IntelliJ and Darcula themes, and less dependence, very simple to use, I think the current one isSwing UIComponent visual effects preferred

Introduce the dependency of FlatLaf and OpenFx:

<dependency>
    <groupId>com.formdev</groupId>
    <artifactId>flatlaf</artifactId>
    <version>1.5</version>
</dependency>
<dependency>
    <groupId>com.formdev</groupId>
    <artifactId>flatlaf-intellij-themes</artifactId>
    <version>1.5</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-media</artifactId>
    <version>11.0.2</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-swing</artifactId>
    <version>11.0.2</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-web</artifactId>
    <version>11.0.2</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-base</artifactId>
    <version>11.0.2</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-graphics</artifactId>
    <version>11.0.2</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>11.0.2</version>
</dependency>
Copy the code

Layout and Implementation

The implementation of the layout is relatively simple:

The final H5 text is rendered in the WebView component (JFXPanel is a JavaFx => Swing adapter, WebView is a JavaFx component, but the outer container used here is all Swing components). The encoding implementation is as follows:

public class MarkdownEditor {

    private static final int W = 1200;
    private static final int H = 1000;
    private static final String TITLE = "markdown editor";

    public static String CONTENT = "
      \n" +
            "<html lang=\"en\">\n" +
            "<head>\n" +
            " 
      \n" +
            "
      \n" +
            " ByteMD example\n" +
            " 
      \n" +
            " 
      \n" +
            " \n" +
            " \n" +
            " \n" +
            "  +
            " .bytemd {\n" +
            " height: calc(100vh - 50px); \n" +
            " }\n" +
            "\n" +
            " .footer {\n" +
            " width: 100%; \n" +
            " height: 30px; \n" +
            " left: 0; \n" +
            " position: absolute; \n" +
            " bottom: 0; \n" +
            " text-align: center; \n" +
            " }\n" +
            " \n" +
            "</head>\n" +
            "<body>\n" +
            "<div class=\"footer\">\n" +
            " bytemd\n" +
            "</div>\n" +
            "<script>\n" +
            " const plugins = [bytemdPluginGfm(), bytemdPluginHighlight()]; \n" +
            " const editor = new bytemd.Editor({\n" +
            " target: document.body,\n" +
            " props: {\n" +
            " value: '# heading\\n\\nparagraph\\n\\n> blockquote',\n" +
            " plugins,\n" +
            " },\n" +
            "}); \n" +
            " editor.$on('change', (e) => {\n" +
            " editor.$set({value: e.detail.value}); \n" +
            "}); \n" +
            "</script>\n" +
            "</body>\n" +
            "</html>";

    static {
        // Initialize the theme
        try {
            UIManager.setLookAndFeel(FlatIntelliJLaf.class.getName());
        } catch (Exception e) {
            throw new IllegalStateException("theme init error", e); }}private static JFrame buildFrame(int w, int h, LayoutManager layoutManager) {
        JFrame frame = new JFrame();
        frame.setLayout(layoutManager);
        frame.setTitle(TITLE);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setSize(w, h);
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        int x = (int) (toolkit.getScreenSize().getWidth() - frame.getWidth()) / 2;
        int y = (int) (toolkit.getScreenSize().getHeight() - frame.getHeight()) / 2;
        frame.setLocation(x, y);
        return frame;
    }

    private static void initAndDisplay(a) {
        // Build the form
        JFrame frame = buildFrame(W, H, new BorderLayout());
        JFXPanel panel = new JFXPanel();
        Platform.runLater(() -> {
            panel.setSize(W, H);
            initWebView(panel, CONTENT);
            frame.getContentPane().add(panel);
        });
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(MarkdownEditor::initAndDisplay);
    }

    private static void initWebView(JFXPanel fxPanel, String content) {
        StackPane root = new StackPane();
        Scene scene = new Scene(root);
        WebView webView = new WebView();
        WebEngine webEngine = webView.getEngine();
        webEngine.setJavaScriptEnabled(true); webEngine.loadContent(content); root.getChildren().add(webView); fxPanel.setScene(scene); }}Copy the code

H5 text from byTEMd native JS implementation example:

All the code is about 120 lines plus comments. Run with JDK11, the result is as follows:

There are currently 2 unresolved issues (or possible ones) :

  • JSThere was a slight delay in triggering the action
  • WebViewComponent initialization is slow

summary

The Oracle JDK has officially announced that the Swing project will no longer be maintained and is likely to be removed from the JDK as usual, perhaps because it does not represent its own value (low emotional intelligence: it does not make money). The layout of Swing development is anti-human. The layout of a Swing project may take more than 90% of the time. The UI design of native components is “ugly”, there are no rich extensions and active communities. Add to that the fact that there are better cross-platform development options out there such as Qt, React Native, Flutter, etc., and Swing’s oblivion is a foregone conclusion. With the exception of JetBrains, Swing ended up as a minority business hobby and a collection of JDK GUI programming enthusiasts.

The Demo source code:

  • local-markdown-editor(https://gitee.com/throwableDoge/local-markdown-editor)

(E-A-20210815 C-1-D)