5

In most text editors and platforms there are two ways of selecting text using the mouse:

  1. The regular, Click+Drag, moves the end of the selection along with the mouse cursor

  2. Double-click+Drag, same as #1 but it starts by selecting one whole word, and then snaps the end of the selection to whole words

In Swing GUI however, #2 does not work as above. It starts correctly by selecting the whole word where the double-click was, but then it does not snap to whole words during selection.

Is there any way to get Swing text fields to behave as 2, with the selection snapping to whole words?

foolo
  • 804
  • 12
  • 22
  • Hm, I see what you mean. This will require more thought – Hovercraft Full Of Eels Feb 02 '20 at 19:32
  • Probably you are looking for: [java - JTextArea - selection behavior on double / triple click + moving mouse - Stack Overflow](https://stackoverflow.com/questions/58690711/jtextarea-selection-behavior-on-double-triple-click-moving-mouse) – aterai Feb 03 '20 at 04:28
  • @aterai Ah, yes, that seems to be the same question (although specifically for JTextArea, i'm using JTextPane, but seems like the solution works for that as well). I didn't find it when searching. – foolo Feb 03 '20 at 09:15
  • Does this answer your question? [JTextArea - selection behavior on double / triple click + moving mouse](https://stackoverflow.com/questions/58690711/jtextarea-selection-behavior-on-double-triple-click-moving-mouse) – foolo Feb 04 '20 at 19:29

1 Answers1

2

You can create a method to calculate the index of where word your selection ends and starts. See below:

int getWordEndPos(String text, int initPos) {
    int i = initPos;
    while(Character.isAlphabetic(text.charAt(i))) {
        i++;
    }
    return i;
}

int getWordStartPos(String text, int initPos) {
    int i = initPos;
    while(Character.isAlphabetic(text.charAt(i))) {
        i--;
    }
    return i+1;
}

Then in your UI (not sure exactly how JTextArea works) you could get the start and end position of your selection, and actually selects the start and end position of their words:

void updateSelection(JTextArea ta) {
    String text = ta.getText();
    int start = ta.getSelectionStart();
    int end = ta.getSelectionEnd();
    start = getWordStartPos(text, start);
    end = getWordEndPos(text, end);
    ta.select(start, end);
}

But where to call the snippet above? You could listen to CarretEvent instead of MouseEvent (see Which event a selection of text trigger in Java JTextArea?):

textArea.addCarretListener((evt) -> updateSelection(textArea));

But another problem arrises: how to know the click count of MouseEvent. You could make an attribute to store it, and then into the mouse event listener, it can be set. The code below tries to put everything toghether:

class UI implements MouseListener, CarretListener {
    JTextArea textArea;
    int clickCount = 0;

    UI() {
        textArea.addCarretListener(this);
        textArea.addMouseListener(this);
        // ...
    }

    @Override
    void mouseClicked(MouseEvent evt) {
        this.clickCount = evt.getClickCount();
        // other stuff
    }

    // other MouseListener methods

    @Override
    void caretUpdate(CaretEvent evt) {
        if (clickCount == 1) updateSelection(textArea);
        // other caret listener stuff
    }

    void updateSelection(JTextArea ta) {
        String text = ta.getText();
        int start = ta.getSelectionStart();
        int end = ta.getSelectionEnd();
        start = getWordStartPos(text, start);
        end = getWordEndPos(text, end);
        ta.select(start, end);
    }
}
LBald
  • 473
  • 2
  • 11
  • Yep, but unfortunately, the hard part is not how to find word boundaries. That can be done in several ways. The tricky part is how to sync it with the selection mechanism, (distinguishing between the single- and the double-click behavior, etc). – foolo Feb 02 '20 at 22:15
  • You could count the clicks with `MouseEvent.getClickCount()`, see https://examples.javacodegeeks.com/desktop-java/awt/event/determine-click-count-example/ . – LBald Feb 02 '20 at 22:25
  • Yes, but MouseEvent.getClickCount() does not return 2 in this case. It only returns 2 for a complete "normal" double click, not when starting a dragging operation by double clicking. – foolo Feb 02 '20 at 22:33
  • Then I think you could test if `getClickCount() == 1` in your pressed or dragged event – LBald Feb 02 '20 at 22:55
  • Yes, but that would detect also single clicks, which means that a single click followed by a regular click+drag, would have the same event sequence as double-click+drag. So then you would need to add some ~500ms timer and also check the XY position do determine whether it is a double click or not, and then it's starting to get very messy, but maybe that's the only possible solution. – foolo Feb 02 '20 at 23:04
  • Good point, I just found a kind of helping question. Instead of listen to MouseEvent, you could listen to carret chage. See https://stackoverflow.com/questions/15147016/which-event-a-selection-of-text-trigger-in-java-jtextarea , and to track the click count in order to verify if it is 1, you can make it "global" to both listener methods with the mouse clicked event. – LBald Feb 02 '20 at 23:21