Autocompletion in codearea in javafx

431 views Asked by At

How could I create a listview at the current caret position in codearea while typing as autocompletion in javafx? So far I locate the word that is currently being typed and I see if that word is contained in the array. This is the code I have so far. Thank you in advance!

String[] keyphrases = new String[]{"int main(){\n}", "cout", "cin"};

((CodeArea)tabPane.getSelectionModel().getSelectedItem().getContent()).textProperty().addListener(new ChangeListener<String>()
                        {
                            @Override
                            public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
                                CodeArea sto = ((CodeArea)tabPane.getSelectionModel().getSelectedItem().getContent());
                                String curr = "";
                                String currFinal = "";
                                for (int i = sto.getAnchor(); i > 0; i--) {
                                    if (sto.getText().charAt(i) == '\n' || sto.getText().charAt(i) == ' ') {
                                        break;
                                    }else {
                                        curr += sto.getText().charAt(i);
                                    }
                                }
                                for (int i = curr.length()-1; i >= 0; i--) {
                                    currFinal += curr.charAt(i);
                                }
                                System.out.println(currFinal);
                                ArrayList<String> fil = new ArrayList<String>();
                                for (int i = 0; i < keyphrases.length; i++) {
                                    if (keyphrases[i].contains(currFinal)) {
                                        fil.add(keyphrases[i]);
                                    }
                                }
                                //display fil as listview in caret position?
                            } 
                        });
2

There are 2 answers

1
Benjamin Sloutsky On BEST ANSWER

Using Sai's answer I created my own solution!

codeArea.textProperty().addListener(new ChangeListener<String>()
    {
        @Override
        public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
            
            
            String curr = "";
            String currFinal = "";
            for (int i = codeArea.getAnchor(); i > 0; i--) {
                if (codeArea.getText().charAt(i) == '\n' || codeArea.getText().charAt(i) == ' ') {
                    break;
                }else {
                    curr += codeArea.getText().charAt(i);
                }
            }
            
            for (int i = curr.length()-1; i >= 0; i--) {
                currFinal += curr.charAt(i);
            }
            
            if (currFinal != "") {
                ArrayList<String> fil = new ArrayList<String>();
                for (int i = 0; i < keyphrases.length; i++) {
                    if (keyphrases[i].contains(currFinal)) {
                        fil.add(keyphrases[i]);
                    }
                }
                System.out.println("Fil " + fil);
                if (popup != null) {
                    popup.hide();
                }
                if (fil.size() > 0) {
                    
                    
                    
                    ListView lop = new ListView();
                    for (int i = 0; i < fil.size(); i++) {
                        lop.getItems().add(fil.get(i));
                    }
                    
                    popup = new Popup();
                 
                    lop.setMaxHeight(80);
                    popup.getContent().addAll(lop);
                    popup.show(codeArea, codeArea.getCaretBounds().get().getMaxX(), codeArea.getCaretBounds().get().getMaxY());
                    codeArea.requestFocus();
                    
                }
                codeArea.requestFocus();
            }else { 
                if (popup != null) {
                    popup.hide();
                }
            }
        }
            
            
    });
0
Sai Dandem On

If you are asking about how to show a ListView at the caret position, please check the below approach. This is a general high level approach of showing the ListView at the current caret position. You can relate the logic and change as per your requirements.

I believe this will give you the required basics about how to approach. Having said that, there can be many other better approaches.

The core idea is to rely on the caret node (Path) bounds, rather than doing the complex computing for finding the location of caret in the text.

enter image description here

import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Path;
import javafx.stage.Popup;
import javafx.stage.Stage;

public class TextAreaCaretPositionDemo extends Application {
    private Bounds caretBoundsInScreen;
    private Node caret;

    @Override
    public void start(Stage stage) throws Exception {
        final VBox root = new VBox();
        root.setSpacing(10);
        root.setPadding(new Insets(10));
        final Scene sc = new Scene(root, 350, 200);
        stage.setScene(sc);
        stage.setTitle("TextArea Caret Position");
        stage.show();

        TextArea textArea = new TextArea() {
            @Override
            protected void layoutChildren() {
                super.layoutChildren();
                if (caret == null) {
                    final Region content = (Region) lookup(".content");
                    // Looking for the caret path node and add a listener to its bounds to keep track of its position in screen.
                    content.getChildrenUnmodifiable().stream()
                            .filter(node -> node instanceof Path)
                            .map(node -> (Path) node)
                            // Find a more better way to find the caret path node
                            .filter(path -> path.getStrokeWidth() == 1 && path.fillProperty().isBound() && path.strokeProperty().isBound())
                            .findFirst().ifPresent(path -> {
                        path.boundsInLocalProperty().addListener((obs, old, bounds) -> {
                            if (bounds.getWidth() > 0 && bounds.getHeight() > 0) {
                                caretBoundsInScreen = path.localToScreen(bounds);
                            }
                        });
                        caret = path;
                    });
                }
            }
        };
        textArea.setWrapText(true);
        VBox.setVgrow(textArea, Priority.ALWAYS);

        ListView<String> list = new ListView<>();
        list.setPrefSize(150,200);
        list.getItems().addAll("One","Two","Three");
        Popup popup = new Popup();
        popup.setAutoHide(true);
        popup.getContent().addAll(list);

        Button show = new Button("Show ListView");
        show.setOnAction(e->{
            popup.show(caret, caretBoundsInScreen.getMinX(), caretBoundsInScreen.getMaxY());
        });
        root.getChildren().addAll(show,textArea);

        textArea.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
    }
}