0

I want in my java code for when the characters co are enter in a individual line in a text area object. For mputer to spell out the word computer and then go to the next line below. You can see exactly what I am looking for in the gif below. I have added some code of what I have tried in my key listener class below.

enter image description here

static JFrame f; 

// text area 
static JTextArea jt;


class CustomKeyListener implements KeyListener{

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            String txt = text11.jt.getText();
            boolean hasText = false;


            for (int i = txt.length() - 1; i >= 0; i--) {

                if (txt.charAt(i) == '\n') break;

                if (Character.isWhitespace(txt.charAt(i))) continue;

                hasText = true;
                break;
            }

            if (hasText) {
               text11.jt.setText(text11.jt.getText() + "     computer");
           } 
        }
    }
  • Does this answer your question? [How to show autocomplete as I type in JTextArea?](https://stackoverflow.com/questions/10873748/how-to-show-autocomplete-as-i-type-in-jtextarea) Or [this](https://stackoverflow.com/questions/15219625/how-would-be-implements-autosugesion-in-jtextarea-swing)? – Nowhere Man Jul 01 '20 at 04:59
  • The code is not the same that codes adds a number to the end of every text. –  Jul 03 '20 at 04:21

1 Answers1

1

Here is my own implementation and how I achieved it:

AutoCompleteTextArea:

import java.awt.BorderLayout;
import java.awt.Dialog.ModalExclusionType;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;

public class AutoCompleteTextArea extends JTextArea implements FocusListener, DocumentListener {
    private static final long serialVersionUID = 1L;
    private AutoCompleteRecommendationsWindow autoCompleteWindow;
    private List<String> words;

    public AutoCompleteTextArea(List<String> words) {
        super();
        init(words);
    }

    public AutoCompleteTextArea(List<String> words, int rows, int columns) {
        super(rows, columns);
        init(words);
    }

    public AutoCompleteTextArea(List<String> words, String text, int rows, int columns) {
        super(text, rows, columns);
        init(words);
    }

    public AutoCompleteTextArea(List<String> words, String text) {
        super(text);
        init(words);
    }

    private void init(List<String> words) {
        this.words = words;
        autoCompleteWindow = new AutoCompleteRecommendationsWindow(this);
        getDocument().addDocumentListener(this);
        addFocusListener(this);
    }

    private void attemptAutoCompleteFromRemoval(DocumentEvent e) throws BadLocationException {
        if (e.getLength() != 1)
            return;

        int pos = e.getOffset();
        String content = getText(0, pos + 1);
        int startPosition = getStartingPositionOfTheWord(pos - 1, content);

        if (pos - startPosition < 3) {
            autoCompleteWindow.setVisible(false);
            return;
        }
        String prefix = content.substring(startPosition + 1);
        showRecommendations(prefix.trim());
    }

    private int getStartingPositionOfTheWord(int offset, String content) {
        int index;
        for (index = offset; index >= 0; index--) {
            if (!Character.isLetter(content.charAt(index))) {
                break;
            }
        }
        return index;
    }

    private void showRecommendations(String prefix) {
        List<String> recommendations = getAutoCompleteRecommendationsForPrefix(prefix);
        if (!recommendations.isEmpty()) {
            autoCompleteWindow.showRecommendations(prefix, recommendations);
            SwingUtilities.invokeLater(() -> autoCompleteWindow.setVisible(true));
        } else {
            autoCompleteWindow.setVisible(false);
        }
    }

    private List<String> getAutoCompleteRecommendationsForPrefix(String prefix) {
        int n = Collections.binarySearch(words, prefix);
        List<String> result = new ArrayList<>();
        if (n < 0 && -n <= words.size()) {
            int indexOfMatchedWord = -n - 1;
            String match = words.get(indexOfMatchedWord);
            if (!match.startsWith(prefix))
                return result;
            result.add(match);
            for (int i = indexOfMatchedWord + 1; i < words.size(); i++) {
                match = words.get(i);
                if (match.startsWith(prefix)) {
                    result.add(match);
                } else
                    break;
            }
            for (int i = indexOfMatchedWord - 1; i >= 0; i--) {
                match = words.get(i);
                if (match.startsWith(prefix)) {
                    result.add(match);
                } else
                    break;
            }
        }
        return result.stream().sorted(Comparator.naturalOrder()).distinct().collect(Collectors.toList());
    }

    private void attemptAutoComplete(DocumentEvent ev) throws BadLocationException {
        if (ev.getLength() != 1) {
            return;
        }

        int pos = ev.getOffset();
        String content = getText(0, pos + 1);
        int startPosition = getStartingPositionOfTheWord(pos, content);

        if (pos - startPosition < 2) {
            autoCompleteWindow.setVisible(false);
            return;
        }

        String prefix = content.substring(startPosition + 1);
        showRecommendations(prefix);
    }

    @Override
    public void focusGained(FocusEvent e) {
    }

    @Override
    public void focusLost(FocusEvent e) {
        autoCompleteWindow.setVisible(false);
    }

    @Override
    public void insertUpdate(DocumentEvent e) {
        try {
            attemptAutoComplete(e);
        } catch (BadLocationException e1) {
        }
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
        try {
            attemptAutoCompleteFromRemoval(e);
        } catch (BadLocationException e1) {
        }
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
    }

    private class AutoCompleteRecommendationsWindow extends JWindow implements KeyListener, MouseListener {
        private static final long serialVersionUID = 2646960455879045456L;
        private JTextArea area;
        private JList<String> wordList;
        private String prefix;

        private AutoCompleteRecommendationsWindow(JTextArea area) {
            super();
            this.area = area;
            setModalExclusionType(ModalExclusionType.APPLICATION_EXCLUDE);
            wordList = new JList<>();
            wordList.addMouseListener(this);
            wordList.setModel(new DefaultListModel<>());

            area.addKeyListener(this);
            getContentPane().setLayout(new BorderLayout());
            JScrollPane sp = new JScrollPane(wordList);
            getContentPane().add(sp, BorderLayout.CENTER);
            pack();
        }

        public void showRecommendations(String prefix, List<String> recommendations) {
            this.prefix = prefix;
            wordList.setVisibleRowCount(Math.min(10, recommendations.size()));

            String longestWord = recommendations.stream().max(Comparator.comparingInt(String::length)).get();
            wordList.setPrototypeCellValue(longestWord + "S"); // Some extra space

            showRecommendationsInWordList(recommendations);
            wordList.setSelectedIndex(0);
            revalidate();
            pack();
        }

        private void showRecommendationsInWordList(List<String> recommendations) {
            DefaultListModel<String> model = (DefaultListModel<String>) wordList.getModel();
            model.removeAllElements();
            recommendations.forEach(model::addElement);
        }

        @Override
        public void setVisible(boolean b) {
            try {
                setLocation(getCursorLocation());
            } catch (BadLocationException e) {
                return;
            }
            super.setVisible(b);
        }

        private Point getCursorLocation() throws BadLocationException {
            Rectangle rect = area.modelToView(Math.max(0, area.getCaretPosition() - 1));
            if (rect == null) // In a test environment is null
                throw new BadLocationException("", 0);

            Point coordinates = rect.getLocation();
            coordinates.x += 10;
            coordinates.y += area.getFontMetrics(area.getFont()).getHeight();
            SwingUtilities.convertPointToScreen(coordinates, area);
            return coordinates;
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyPressed(KeyEvent e) {
            if (!isVisible())
                return;
            if (isDownArrowKey(e) || isUpArrowKey(e)) {
                String value = wordList.getSelectedValue();

                if (isDownArrowKey(e) && wordList.getSelectedIndex() < wordList.getModel().getSize() - 1) {
                    value = wordList.getModel().getElementAt((wordList.getSelectedIndex() + 1));
                } else if (isUpArrowKey(e) && wordList.getSelectedIndex() > 0) {
                    value = wordList.getModel().getElementAt((wordList.getSelectedIndex() - 1));
                }
                wordList.setSelectedValue(value, true);
                e.consume();
            } else if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                addSelectedWordAndClose();
                e.consume();
            } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                setVisible(false);
            }
        }

        private void addSelectedWordAndClose() {
            int position = area.getCaretPosition();
            area.insert((wordList.getSelectedValue() + " ").substring(prefix.length()), position);
            setVisible(false);
        }

        @Override
        public void keyReleased(KeyEvent e) {

        }

        private boolean isDownArrowKey(KeyEvent e) {
            return e.getKeyCode() == KeyEvent.VK_DOWN;
        }

        private boolean isUpArrowKey(KeyEvent e) {
            return e.getKeyCode() == KeyEvent.VK_UP;
        }

        @Override
        public void mouseClicked(MouseEvent e) {
        }

        @Override
        public void mousePressed(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            addSelectedWordAndClose();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }
    }
}
  • It takes a List<String> in constructor which represents your dictionary. Be careful that the dictionary must be sorted since it uses binary search to find the recommendation words for the auto-complete.
  • It allows you to navigate recommended words with arrow keys and

The demo:

import java.awt.BorderLayout;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class Demo {
    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Auto complete Demo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());

            List<String> dictionary = Arrays.asList("computer", "canada", "controversial", "conversation", "construct");
            Collections.sort(dictionary);

            AutoCompleteTextArea autoCompleteTextArea = new AutoCompleteTextArea(dictionary, 20, 50);
            autoCompleteTextArea.setFont(new JLabel().getFont()); // Windows laf text area font does not look good
            frame.add(autoCompleteTextArea);

            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }
}

Preview:

preview


In order to do what you are asking in the comments, you will have to stop consuming ( keyEvent.consume ) the VK_ENTER key.

In AutoCompleteRecommendationsWindow#keyPressed there is this part:

else if (e.getKeyCode() == KeyEvent.VK_ENTER) {
    addSelectedWordAndClose();
    e.consume();
}

consume does exactly this. It consumes the key event so the line does not change. If you remove it, when VK_ENTER is pressed, the cursor will move to the next line.

George Z.
  • 6,643
  • 4
  • 27
  • 47
  • In your code can you add all the libraries you imported. –  Jul 06 '20 at 15:16
  • Wow! Works flawlessly! Just to make it more useful, you can add comments in the code. – Arvind Kumar Avinash Jul 06 '20 at 17:46
  • code is working however I want when the user enters the word computer it goes to the very next line below it. Right now when you hit enter on the auto complete the code stays on the same line. Just like my gif above. If you can do that I will give you the bounty. –  Jul 06 '20 at 23:10
  • I mean I want the code to function like the gif above –  Jul 06 '20 at 23:15