Reputation: 113
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.
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
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
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