Joey Deguzman
Joey Deguzman

Reputation: 113

Having to automatically highlight specific String from a text field to a text area

I am now wondering how to automatically highlight the text within a text area. I have researched further and found a method to do so however I am not sure how it can still highlight a String automatically after the user enters search.

enter image description here

Note: Ignore the radio buttons

Here is the method I tried within the action event when clicking the search button

 @FXML
 private void searchButton(ActionEvent actionEvent){
    String key = keyField.getText();
    String field = textField.getText();
    System.out.println(key);
    System.out.println(field);

        textField.getText();
        if (textField.getText().equals(key)) {
            textField.setStyle("-fx-highlight-fill: yellow; -fx-highlight-text-fill: black; -fx-font-size: 12px; ");
            textField.setEditable(false);
            textField.addEventFilter(MouseEvent.ANY, new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent t) {
                    t.consume();
                }
            });

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

    ToggleGroup group = new ToggleGroup();
    rabinKarpp.setToggleGroup(group);
    kmp.setToggleGroup(group);
    naive.setToggleGroup(group);

    ArrayList indexes = new ArrayList();
    if (rabinKarpp.isSelected()){
        indexes = RabinKarp.process(field, key);
    }
    else if (kmp.isSelected()){
        indexes = KMPAlgo.process(field, key);
    }
    else if (naive.isSelected()){
        indexes = NaiveAlgo.process(field, key);
    }
    else if(!naive.isSelected() && !kmp.isSelected() && !rabinKarpp.isSelected()) {
        Alert alert = new Alert(Alert.AlertType.WARNING, "Select an algorithm first before proceeding.",ButtonType.OK);
        alert.showAndWait();
    }

}

the expected output should be the "hello" from "hello everyone" to be highlighted but I'm still stumped on how to do so.

EDIT:

The code now looks like this but still the highlights will not turn up sadly.

@FXML protected void searchButton(ActionEvent actionEvent){

    String key = keyField.getText();
    String field = textField.getText();
    System.out.println(key);
    System.out.println(field);

    ToggleGroup group = new ToggleGroup();
    rabinKarpp.setToggleGroup(group);
    kmp.setToggleGroup(group);
    naive.setToggleGroup(group);

    ArrayList indexes = new ArrayList();
    if (rabinKarpp.isSelected()){
        indexes = RabinKarp.process(field, key);
    }
    else if (kmp.isSelected()){
        indexes = KMPAlgo.process(field, key);
    }
    else if (naive.isSelected()){
        indexes = NaiveAlgo.process(field, key);
    }
    else if(!naive.isSelected() && !kmp.isSelected() && !rabinKarpp.isSelected()) {
        Alert alert = new Alert(Alert.AlertType.WARNING, "Select an algorithm first before proceeding.",ButtonType.OK);
        alert.showAndWait();
    }
}

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
    List<List<Integer>> locations = new ArrayList();
    keyField.textProperty().addListener((o, oldValue, newValue) -> {
        Pattern pattern = Pattern.compile(newValue);
        Matcher matcher = pattern.matcher(textField.getText());

        locations.clear();
        if (newValue.isEmpty()) {
            // no highlighting for the empty search string
            textField.deselect();
        } else {
            int index = textField.getText().indexOf(newValue);
            if (index < 0) {
                // text not found
                textField.deselect();
            } else {
                while(matcher.find())
                {
                    List<Integer> tempList = new ArrayList<>();
                    tempList.add(matcher.start());
                    tempList.add(matcher.end());

                    locations.add(tempList);
                }
            }
        }
    });
    AtomicInteger currentIndex = new AtomicInteger(-1);
    downButton.setOnAction((t) -> {
        if(currentIndex.get() >= -1 && currentIndex.get() < locations.size() - 1)
        {
            textField.selectRange(locations.get(currentIndex.incrementAndGet()).get(0), locations.get(currentIndex.get()).get(1));
        }
    });

    upButton.setOnAction((t) -> {
        if(currentIndex.get() > 0 && currentIndex.get() <= locations.size())
        {
            textField.selectRange(locations.get(currentIndex.decrementAndGet()).get(0), locations.get(currentIndex.get()).get(1));
        }
    });
}

Upvotes: 0

Views: 617

Answers (2)

SedJ601
SedJ601

Reputation: 13859

I would like to add on to @fabian's answer. If you want to find multiple occurrences one at a time, you can find all the matches in the string and save their indexes to a list. The list can be used to step through each occurrence.

I have added 4 occurrences of hello to fabian's original string. You can type hello into the TextField and hit up and then down, to reach each occurrence.

    TextArea textArea = new TextArea();
    VBox.setVgrow(textArea, Priority.ALWAYS);
    Random random = new Random();
    for (int l = 0; l < 10; l++) {
        for (int i = 0; i < 300; i++) {
            textArea.appendText(Character.toString('a' + random.nextInt('z'- 'a' + 1)));
        }
        textArea.appendText("\n");
    }

    textArea.replaceText(0, 5, "hello");
    textArea.replaceText(20, 25, "hello");
    textArea.replaceText(30, 35, "hello");
    textArea.replaceText(textArea.getText().length() - 6, textArea.getText().length() -1, "hello");

    TextField textField = new TextField();
    List<List<Integer>> locations = new ArrayList();

    textField.textProperty().addListener((o, oldValue, newValue) -> {
        Pattern pattern = Pattern.compile(newValue);
        Matcher matcher = pattern.matcher(textArea.getText());

        locations.clear();
        if (newValue.isEmpty()) {
            // no highlighting for the empty search string
            textArea.deselect();
        } else {                
            int index = textArea.getText().indexOf(newValue);
            if (index < 0) {
                // text not found
                textArea.deselect();
            } else {
                while(matcher.find())
                {
                    List<Integer> tempList = new ArrayList<>();
                    tempList.add(matcher.start());
                    tempList.add(matcher.end());

                    locations.add(tempList);
                }
            }
        }
    });

    AtomicInteger currentIndex = new AtomicInteger(-1);
    Button btnDown = new Button("Down");
    btnDown.setOnAction((t) -> {
        if(currentIndex.get() >= -1 && currentIndex.get() < locations.size() - 1)
        {
            textArea.selectRange(locations.get(currentIndex.incrementAndGet()).get(0), locations.get(currentIndex.get()).get(1));
        }
    });

    Button btnUp = new Button("Up");
    btnUp.setOnAction((t) -> {
        if(currentIndex.get() > 0 && currentIndex.get() <= locations.size())
        {
            textArea.selectRange(locations.get(currentIndex.decrementAndGet()).get(0), locations.get(currentIndex.get()).get(1));
        }
    });
    Scene scene = new Scene(new VBox(textArea, textField, new HBox(btnDown, btnUp)));

    stage.setScene(scene);
    stage.show();

Upvotes: 2

fabian
fabian

Reputation: 82451

Simply listen to the text property of the TextField for this purpose. Note that you can only select a single range using TextArea. If you need to highlight multiple occurances you may want to use a TextFlow and it's rangeShape method as suggested by @Slaw.

@Override
public void start(Stage stage) throws IOException {
    TextArea textArea = new TextArea();
    VBox.setVgrow(textArea, Priority.ALWAYS);
    Random random = new Random();
    for (int l = 0; l < 10; l++) {
        for (int i = 0; i < 300; i++) {
            textArea.appendText(Character.toString('a' + random.nextInt('z'- 'a' + 1)));
        }
        textArea.appendText("\n");
    }

    TextField textField = new TextField();
    textField.textProperty().addListener((o, oldValue, newValue) -> {
        if (newValue.isEmpty()) {
            // no highlighting for the empty search string
            textArea.deselect();
        } else {
            int index = textArea.getText().indexOf(newValue);
            if (index < 0) {
                // text not found
                textArea.deselect();
            } else {
                // select first occurance
                textArea.selectRange(index, index + newValue.length());
            }
        }
    });

    Scene scene = new Scene(new VBox(textArea, textField));

    stage.setScene(scene);
    stage.show();
}

Using fxml the proper place to register a listener like this would be the initialize method.

Upvotes: 3

Related Questions