Что такое JavaFX-эквивалент JSyntaxPane для создания редактора кода?

Ранее в Swing я использовал JSyntaxPane для создания крошечного редактора исходного кода Java. Для практики я решил переделать весь проект на JavaFX и добавить поддержку большего количества языков. Желательно столько, сколько смогу.

Однако, похоже, ничего похожего на JSyntaxPane нет.

Небольшое исследование привело меня к блогуTom Schindl. > где он сделал просмотрщик исходного кода с правильной подсветкой синтаксиса. Нет поддержки редактирования, к сожалению.

Затем есть блог JewelSea, но на скриншоте это похоже на метод SO type-and-preview. Не то, что нужно в редакторе кода.

Опять же, из JFXperience я обнаружил, что выделение, отступ и редактирование панели/узла будут доступны в JavaFX 8, а также это позволит встроить Swing в Java.

Какие другие варианты у меня есть до тех пор?

Я знаю, что JavaFX может взаимодействовать с JavaScript, поэтому могу ли я использовать какую-нибудь библиотеку JavaScript, чтобы сделать то же самое?


person An SO User    schedule 26.10.2013    source источник


Ответы (4)


В настоящее время я использую Ace Editor в своем проекте с открытым исходным кодом через WebEngine. Вот демонстрация Kitchen Sink.

ОБНОВЛЕНИЕ

Возможный подход к взаимодействию JS/FX в текущей версии JDK:

  • Напишите часть приложения/виджета JS, протестируйте ее отдельно. Если вы собираетесь встроить только виджет редактора, это может быть пустая веб-страница с <div>, которая является вашим редактором.
  • Тогда план для сценария «получить текст из JS» может быть таким: «вызвать функцию JS из Java, она получит текст из элемента редактора и вызовет часть Java с текстом, переданным в качестве аргумента String для метода». .
  • Изучите привязку Java-JS, т.е. обратный вызов WebView из Javascript
  • Вставьте FirebugLite для отладки вашего JS из WebView. Единственная версия, которая работала для меня, была:

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

Несколько общих советов: старайтесь избегать сложных вызовов JS-to-Java. Я отправил пару сообщений об ошибках команде JavaFX, потому что некоторые простые вещи, такие как переопределение метода, у меня не работали. Избегайте передачи объектов Java в JS — хотя это теоретически возможно, я всегда заканчивал сбоями приложений. Итак, теперь я передаю JSON и конвертирую его в объекты с обеих сторон.

Вы можете посмотреть рабочий пример здесь приложения AngularJS/JavaFX. Он находится в состоянии предварительной альфа-версии, поэтому может даже не запускаться на вашем компьютере, но его можно рассматривать как доказательство концепции настольного приложения AngularJS.

person Andrey Chaschev    schedule 26.10.2013
comment
Можете ли вы сказать мне, как работать с любой библиотекой javascript в javafx? Я имею в виду обобщенные шаги. Мне начинает нравиться Ace Editor - person An SO User; 26.10.2013
comment
Спасибо. Я свяжусь с вами, как только мне понадобится помощь. - person An SO User; 27.10.2013
comment
да. Я пропустил эту "самую первую" часть =) - person Andrey Chaschev; 27.10.2013
comment
Вы ведете блог? Если это так, вы можете опубликовать пошаговые инструкции к этому. Очень нужно =)) - person An SO User; 27.10.2013
comment
Я думаю об этом, но у меня сейчас нет на это времени. - person Andrey Chaschev; 27.10.2013
comment
ой... похоже, придется провести ночное исследование, ха-ха! ;-) - person An SO User; 27.10.2013
comment
Вы уговорили меня на это. Постараюсь создать свой первый пост на эту тему! Возможно, это могло бы привлечь некоторый трафик к Медведю. - person Andrey Chaschev; 27.10.2013
comment
Начал блог и сегодня добавит сообщение. Будь первым гостем! :-) - person Andrey Chaschev; 27.10.2013
comment
Видел блог! Свяжите свой SO с вашим блогом. ;-) С нетерпением жду поста редактора кода. - person An SO User; 27.10.2013
comment
stackoverflow.com/ вопросы/19619126/ - person An SO User; 27.10.2013
comment
давайте продолжим обсуждение в чате - person An SO User; 27.10.2013

Существует RichTextFX, который позволяет вам выделять. Посмотрите пример ключевых слов Java.

Обратите внимание, что для этого требуется JDK8.

person Tomas Mikula    schedule 01.02.2014
comment
Я пытаюсь добавить codeArea во вкладку под разделенной панелью и применить файл css к codeArea. Номера строк отсутствуют в области кода. Я знаю, что вы применили файл css к сцене, в этом может быть разница, но в любом случае я могу использовать lineno в codeArea. :) Кстати, спасибо за RichTextFX, это очень красиво и очень полезно. :) - person DeepSidhu1313; 21.10.2014
comment
@DeepSidhu1313 DeepSidhu1313 Да, вы можете использовать номера строк в CodeArea, но вы должны включить их с помощью codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea)). Пожалуйста, используйте систему отслеживания проблем от RichTextFX для получения дополнительной помощи. - person Tomas Mikula; 22.10.2014

Образец редактора, который я разместил, не является методом предварительного просмотра типов, это редактор JavaScript, встроенный (codemirror) в приложение JavaFX с использованием WebKit. . Вы можете найти соответствующий источник здесь или обновленную версию мини-IDE в концептуальный проект.

person jewelsea    schedule 26.10.2013
comment
Я так жду, когда Java 8 выйдет стабильно. SwingNode кажется блаженством. У меня будут все мои любимые вещи Swing в JavaFX =) и о... поддерживает ли это отступы? - person An SO User; 26.10.2013
comment
Не уверен, что именно вы имеете в виду под отступом, но ответ, вероятно, да, он довольно функционален для основных задач редактирования. Вы должны просто запустить код и сами убедиться, можно ли его сделать в соответствии с вашими потребностями. - person jewelsea; 26.10.2013
comment
Кажется, мне не хватает части о том, как добавить CodeMirror в WebView. - person An SO User; 26.10.2013

Я адаптировал этот код для RichTextFX, чтобы создать собственную автономную область TextCodeArea. Вы должны иметь возможность просто добавить это в свое приложение и работать с ним. Вам просто нужно передать ему узел AnchorPane для присоединения.

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();
    }
}

Также не забудьте включить свой CSS в функцию Main приложения JavaFX:

    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);
}
person Pixel    schedule 06.11.2020