What is JavaFX equivalent of JSyntaxPane for making a code editor?

前端 未结 4 1627
悲&欢浪女
悲&欢浪女 2021-02-06 05:27

Previously in Swing, I have used the JSyntaxPane for making a tiny Java source editor. For practice, I decided to redo the entire project in JavaFX and adding suppo

相关标签:
4条回答
  • 2021-02-06 05:50

    I adapted this code for RichTextFX to create my own self contained TextCodeArea. You should be able to just add this to your application and run with it. You just nee to pass it an AnchorPane node to attach itself to.

    public class TextCodeArea {
    
        private static final String[] KEYWORDS = new String[] {
                "abstract", "assert", "boolean", "break", "byte",
                "case", "catch", "char", "class", "const",
                "continue", "default", "do", "double", "else",
                "enum", "extends", "final", "finally", "float",
                "for", "goto", "if", "implements", "import",
                "instanceof", "int", "interface", "long", "native",
                "new", "package", "private", "protected", "public",
                "return", "short", "static", "strictfp", "super",
                "switch", "synchronized", "this", "throw", "throws",
                "transient", "try", "void", "volatile", "while"
        };
    
        private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b";
        private static final String PAREN_PATTERN = "\\(|\\)";
        private static final String BRACE_PATTERN = "\\{|\\}";
        private static final String BRACKET_PATTERN = "\\[|\\]";
        private static final String SEMICOLON_PATTERN = "\\;";
        private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\"";
        private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/";
        private static final String ASSIGNMENT_PATTERN = "\\s+\\w+?\\s+=" + "|" + "\\s+\\w+\\[.*\\]?\\s+=";
    
        private static final Pattern PATTERN = Pattern.compile(
                "(?<KEYWORD>" + KEYWORD_PATTERN + ")"
                        + "|(?<PAREN>" + PAREN_PATTERN + ")"
                        + "|(?<BRACE>" + BRACE_PATTERN + ")"
                        + "|(?<BRACKET>" + BRACKET_PATTERN + ")"
                        + "|(?<SEMICOLON>" + SEMICOLON_PATTERN + ")"
                        + "|(?<STRING>" + STRING_PATTERN + ")"
                        + "|(?<COMMENT>" + COMMENT_PATTERN + ")"
                        + "|(?<ASSIGNMENT>" + ASSIGNMENT_PATTERN + ")"
        );
    
        private CodeArea codeArea;
    
        public  TextCodeArea(AnchorPane pane) {
            codeArea = new CodeArea();
    
            VirtualizedScrollPane sp = new VirtualizedScrollPane(codeArea);
            pane.getChildren().add(sp);
            AnchorPane.setLeftAnchor(sp, 0.0);
            AnchorPane.setRightAnchor(sp, 0.0);
            AnchorPane.setBottomAnchor(sp, 0.0);
            AnchorPane.setTopAnchor(sp, 0.0);
            codeArea.prefWidthProperty().bind(pane.widthProperty());
            codeArea.prefHeightProperty().bind(pane.heightProperty());
            codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
            Subscription cleanupWhenNoLongerNeedIt = codeArea.multiPlainChanges()
                    .successionEnds(java.time.Duration.ofMillis(50))
                    .subscribe(ignore -> codeArea.setStyleSpans(0, computeHighlighting(codeArea.getText())));
            final Pattern whiteSpace = Pattern.compile( "^\\s+" );
            codeArea.addEventHandler( KeyEvent.KEY_PRESSED, key -> {
                if (key.getCode() == KeyCode.ENTER) {
                    int pos = codeArea.getCaretPosition();
                    int par = codeArea.getCurrentParagraph();
                    Matcher matcher = whiteSpace.matcher(codeArea.getParagraph(par-1).getSegments().get(0));
                    if (matcher.find()) Platform.runLater(() -> codeArea.insertText(pos, matcher.group()));
                }
            });
    //        cleanupWhenNoLongerNeedIt.unsubscribe();    // to stop and clean up
        }
    
        private static StyleSpans<Collection<String>> computeHighlighting(String text) {
            int lastKwEnd = 0;
            Matcher matcher = PATTERN.matcher(text);
            StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
    
            while(matcher.find()) {
                String styleClass =
                        matcher.group("KEYWORD") != null ? "keyword" :
                                matcher.group("PAREN") != null ? "paren" :
                                        matcher.group("BRACE") != null ? "brace" :
                                                matcher.group("BRACKET") != null ? "bracket" :
                                                        matcher.group("SEMICOLON") != null ? "semicolon" :
                                                                matcher.group("STRING") != null ? "string" :
                                                                        matcher.group("COMMENT") != null ? "comment" :
                                                                                matcher.group("ASSIGNMENT") != null ? "assignment" :
                                                                                        null; /* never happens */ assert styleClass != null;
                spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
                spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
                lastKwEnd = matcher.end();
            }
            spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
            return spansBuilder.create();
        }
    }
    

    Also make sure to include your CSS in your JavaFX application Main function:

        scene.getStylesheets().add(getClass().getResource("../java-keywords.css").toExternalForm());
    
    .styled-text-area {
        -fx-font-size: 18;
        -fx-background-color: rgb(0, 27, 51);
    }
    
    .styled-text-area .caret {
        -fx-stroke: white;
    }
    
    .styled-text-area .text{
        -fx-fill:white;
    }
    
    .styled-text-area .line {
        -fx-fill: black;
    }
    
    .styled-text-area .text.assignment {
        -fx-fill: orange;
        -fx-font-weight: bold;
    }
    
    .styled-text-area .text.keyword {
        -fx-fill: rgb(110, 252, 187);
        -fx-font-weight: bold;
    }
    
    .styled-text-area .text.semicolon {
        -fx-fill: rgb(110, 252, 187);
        -fx-font-weight: bold;
    }
    
    .styled-text-area .text.paren {
        -fx-fill: yellow;
        -fx-font-weight: bold;
    }
    
    .styled-text-area .text.bracket {
        -fx-fill: white;
        -fx-font-weight: bold;
    }
    
    .styled-text-area .text.brace {
        -fx-fill: yellow;
        -fx-font-weight: bold;
    }
    
    .styled-text-area .text.string {
        -fx-fill: rgb(58,213,11);
    }
    
    .styled-text-area .text.comment {
        -fx-fill: rgb(0, 200, 255);
    }
    
    .paragraph-box:has-caret{
        -fx-background-color: rgb(50, 77, 101);
    }
    
    0 讨论(0)
  • 2021-02-06 05:59

    The editor sample I posted is not a type and preview method, it's a JavaScript editor embedded (codemirror) into a JavaFX application using WebKit. You can find the related source here or an updated version for a mini-IDE in the conception project.

    0 讨论(0)
  • 2021-02-06 06:00

    There's RichTextFX which lets you do the highlighting. Check out the Java Keywords example.

    Note that it requires JDK8.

    0 讨论(0)
  • 2021-02-06 06:08

    I am currently using Ace Editor in my open source project via the WebEngine. Here is the Kitchen Sink demo.

    UPDATE

    A possible approach to JS/FX interaction as of current JDK version:

    • Write the JS app/widget part, test it standalone. If you only intending to embed an editor widget, then it could be an empty web page with a <div> which is your editor.
    • Then a plan for a 'get text from JS' scenario might be like this: 'call the JS function from Java, it will get the text from the editor element and call back the Java part with text passed as String argument for a method'.
    • Learn the Java-JS binding - i.e. WebView callback from Javascript
    • Embed FirebugLite to debug your JS from the WebView. The only version which worked for me was:

      <script src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'>

    Some general advices - try to avoid complexity in JS-to-Java calls. I filed a couple of bugs to the JavaFX team because some simple things like overriding a method didn't work for me. Avoid passing Java objects to JS - though it is theoretically possible, I always ended up with application crashes. So now I am passing JSON and convert it to objects on both sides.

    You may have a look at a working example here of an AngularJS/JavaFX application. It's in a pre-alpha state, so it may not even launch on your machine, but can be seen as proof of concept of an AngularJS desktop app.

    0 讨论(0)
提交回复
热议问题