Выделение строк в JavaFX TextArea

Мы используем JavaFX TextArea контроля в нашем приложении и пытается интегрировать его с Jazzy Spell Check API - например, когда пользователь вводит неправильное слово, которого нет в словаре, такое слово будет выделено.

Есть ли способ выделить слово в указанном элементе управления? Я не видел вариантов для этого в JavaDocs, так что если кто-то может предложить подход?

Я думаю, можно было бы использовать HTMLEditor составить и покрасить слова по-разному с <font face="red=>wrongWord</font>, Это, однако, приносит много разных проблем с проверкой орфографии, таких как теги html и количество слов.

5 ответов

Решение

Элемент управления JavaFX TextArea (по состоянию на 2.0.2) не поддерживает редактирование форматированного текста, в котором смешаны стили текста (шрифты и т. Д.).

Вы можете выделить непрерывные строки символов в TextArea, манипулируя selectRange TextArea, как в следующем примере:

public class TextHighlight extends Application {
  public static void main(String[] args) { Application.launch(args); }
  @Override public void start(Stage stage) {
    final TextArea text = new TextArea("Here is some textz to highlight");
    text.setStyle("-fx-highlight-fill: lightgray; -fx-highlight-text-fill: firebrick; -fx-font-size: 20px;");
    text.setEditable(false);
    text.addEventFilter(MouseEvent.ANY, new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent t) { t.consume(); }
    });

    stage.setScene(new Scene(text));
    stage.show();

    Platform.runLater(new Runnable() {
      @Override public void run() { text.selectRange(13, 18); }
    });
  }
}

Вы можете использовать приведенный выше код в качестве основы для переключения TextArea в режим только для чтения во время проверки орфографии. Реализуйте подсказку, чтобы найти и исправить каждое слово по очереди, пока проверка орфографии не будет завершена. Выполните запрос в отдельном диалоговом окне или панели. Демонстрация Jazzy, кажется, работает следующим образом: http://jazzy.sourceforge.net/demo.html, поэтому преобразование Swing UI в JavaFX должно быть довольно простым.


В качестве альтернативы вы можете использовать элемент управления JavaFX WebView, чтобы обернуть любую из множества программ проверки правописания на основе javascript/html (например, http://www.javascriptspellcheck.com/), используя технику, аналогичную показанной здесь: http://jewelsea.wordpress.com/2011/12/11/codemirror-based-code-editor-for-javafx/.

RichTextFX позволяет добавлять стиль к текстовым диапазонам.

С JavaFX 8 вы можете использовать TextFlow

Вы можете определить определенные классы стилей для полужирного, красного, зеленого или любого типа текстов и расположить их в TextFlow, назначая каждый класс стилей каждому тексту.

Это возможно... вроде

Я знаю, что этот вопрос решен, но я нашел способ исправить проблему и подумал, что опубликую его для других людей, которые также наткнулись на этот пост. Это немного странно, но работает в крайнем случае, если вам нужно выделить текст в TextArea и не хотите принимать неудовлетворительный "невозможный" ответ.

  1. Получить цвет фона TextArea и цвет переднего плана шрифта текста
  2. Вычислить максимальный контраст между цветом переднего плана и фоном
  3. Поместите прямоугольник вокруг желаемого текста
  4. Установите цвет прямоугольника на цвет фона
  5. Установите режим смешивания прямоугольника в корзину с максимальным контрастом
  6. Отключить прямоугольник

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

Вы можете слегка настроить этот метод, если желательны различные цвета.

Я могу опубликовать детали реализации, если люди заинтересованы, скептически или, если это сложно, работать.

У меня есть подобное требование, и ответ @en_Knight довольно многообещающий для меня. Он только что представил теоретические детали. Основываясь на его мнениях, я подумал попробовать, и вот чем я закончил.

Есть много случаев, которые все еще должны быть обработаны. Но, по крайней мере, это должно дать достаточно представления о том, о чем говорит @en_Knight.

Пожалуйста, проверьте приведенную ниже демонстрационную версию:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.effect.BlendMode;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class HighlightableTextAreaDemo extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        VBox root = new VBox();
        root.setSpacing(10);
        root.setPadding(new Insets(10));
        Scene sc = new Scene(root, 600, 600);
        stage.setScene(sc);
        stage.show();


        final HighlightableTextArea highlightableTextArea = new HighlightableTextArea();
        highlightableTextArea.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
        highlightableTextArea.getTextArea().setWrapText(true);
        highlightableTextArea.getTextArea().setStyle("-fx-font-size: 20px;");
        VBox.setVgrow(highlightableTextArea,Priority.ALWAYS);

        Button highlight = new Button("Highlight");
        TextField stF = new TextField("40");
        TextField enF = new TextField("50");
        HBox hb = new HBox(highlight,stF,enF);
        hb.setSpacing(10);
        highlight.setOnAction(e->{highlightableTextArea.highlight(Integer.parseInt(stF.getText()), Integer.parseInt(enF.getText()));});

        Button remove = new Button("Remove Highlight");
        remove.setOnAction(e->highlightableTextArea.removeHighlight());

        Label lbl = new Label("Resize the window to see if the highlight is moving with text");
        lbl.setStyle("-fx-font-size: 17px;-fx-font-style:italic;");
        HBox rb = new HBox(remove,lbl);
        rb.setSpacing(10);

        root.getChildren().addAll(hb,rb,highlightableTextArea);
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

    /**
     * Custom TextArea Component.
     */
    class HighlightableTextArea extends StackPane {
        final TextArea textArea = new TextArea();
        int highlightStartPos = -1;
        int highlightEndPos = -1;
        boolean highlightInProgress = false;

        final Rectangle highlight = new Rectangle();

        private StringProperty text = new SimpleStringProperty();

        private Group selectionGroup;

        public final String getText() {
            return text.get();
        }

        public final void setText(String value) {
            text.set(value);
        }

        public final StringProperty textProperty() {
            return text;
        }

        public HighlightableTextArea() {
            highlight.setFill(Color.RED);
            highlight.setMouseTransparent(true);
            highlight.setBlendMode(BlendMode.DARKEN);

            textArea.textProperty().bindBidirectional(text);
            getChildren().add(textArea);
            setAlignment(Pos.TOP_LEFT);
            textArea.widthProperty().addListener((obs, oldVal, newVal) -> {
                if (highlightStartPos > -1 && highlightEndPos > -1 && selectionGroup != null) {
                    highlightInProgress = true;
                    textArea.selectRange(highlightStartPos, highlightEndPos);
                    Bounds bounds = selectionGroup.getBoundsInLocal();
                    updateHightlightBounds(bounds);
                }
            });
        }

        private void updateHightlightBounds(Bounds bounds) {
            if (bounds.getWidth() > 0) {
                if (!getChildren().contains(highlight)) {
                    getChildren().add(highlight);
                }
                highlight.setTranslateX(bounds.getMinX() + 1);
                highlight.setTranslateY(bounds.getMinY() + 1);
                highlight.setWidth(bounds.getWidth());
                highlight.setHeight(bounds.getHeight());
                Platform.runLater(() -> {
                    textArea.deselect();
                    highlightInProgress = false;
                });
            }
        }

        public TextArea getTextArea() {
            return textArea;
        }

        @Override
        protected void layoutChildren() {
            super.layoutChildren();
            if (selectionGroup == null) {
                final Region content = (Region) lookup(".content");
                // Looking for the Group node that is responsible for selection
                content.getChildrenUnmodifiable().stream().filter(node -> node instanceof Group).map(node -> (Group) node).filter(grp -> {
                    boolean notSelectionGroup = grp.getChildren().stream().anyMatch(node -> !(node instanceof Path));
                    return !notSelectionGroup;
                }).findFirst().ifPresent(n -> {
                    n.boundsInLocalProperty().addListener((obs, old, bil) -> {
                        if (highlightInProgress) {
                            updateHightlightBounds(bil);
                        }
                    });
                    selectionGroup = n;
                });
            }
        }

        public void highlight(int startPos, int endPos) {
            highlightInProgress = true;
            highlightStartPos = startPos;
            highlightEndPos = endPos;
            textArea.selectRange(startPos, endPos);
        }

        public void removeHighlight() {
            textArea.deselect();
            getChildren().remove(highlight);
            highlightStartPos = -1;
            highlightEndPos = -1;
        }

    }

}
Другие вопросы по тегам