Question

Goal

I'm trying to implement "select on focus" behavior for an Eclipse Text control:

  • When the control is already focused:

    • clicking and dragging behaves normally
  • When the control is not focused:

    • clicking and dragging to select text behaves normally

    • clicking without selecting text will select all text

    • giving focus using the keyboard will select all text

Problem

  • Simply selecting all text for SWT.FocusIn doesn't select text when focusing by mouse click.

  • SWT.FocusIn is fired before SWT.MouseDown, so there's no way to tell if the control already had focus when the user presses the mouse down.

Questions

  1. Why does Eclipse fire the events in that order? That doesn't make any sense to me. Is it a restriction of some supported OS?

  2. Is there some workaround I can use to implement this functionality?

Was it helpful?

Solution

Someone in #eclipse linked me to an Eclipse bug raised a long time ago: Can't use focus listener to select all text

Using one of the suggestions there, I came up with the following solution (works in Windows, untested on other platforms):

/**
 * This method adds select-on-focus functionality to a {@link Text} component.
 * 
 * Specific behavior:
 *  - when the Text is already focused -> normal behavior
 *  - when the Text is not focused:
 *    -> focus by keyboard -> select all text
 *    -> focus by mouse click -> select all text unless user manually selects text
 * 
 * @param text
 */
public static void addSelectOnFocusToText(Text text) {
  Listener listener = new Listener() {

    private boolean hasFocus = false;
    private boolean hadFocusOnMousedown = false;

    @Override
    public void handleEvent(Event e) {
      switch(e.type) {
        case SWT.FocusIn: {
          Text t = (Text) e.widget;

          // Covers the case where the user focuses by keyboard.
          t.selectAll();

          // The case where the user focuses by mouse click is special because Eclipse,
          // for some reason, fires SWT.FocusIn before SWT.MouseDown, and on mouse down
          // it cancels the selection. So we set a variable to keep track of whether the
          // control is focused (can't rely on isFocusControl() because sometimes it's wrong),
          // and we make it asynchronous so it will get set AFTER SWT.MouseDown is fired.
          t.getDisplay().asyncExec(new Runnable() {
            @Override
            public void run() {
              hasFocus = true;
            }
          });

          break;
        }
        case SWT.FocusOut: {
          hasFocus = false;
          ((Text) e.widget).clearSelection();

          break;
        }
        case SWT.MouseDown: {
          // Set the variable which is used in SWT.MouseUp.
          hadFocusOnMousedown = hasFocus;

          break;
        }
        case SWT.MouseUp: {
          Text t = (Text) e.widget;
          if(t.getSelectionCount() == 0 && !hadFocusOnMousedown) {
            ((Text) e.widget).selectAll();
          }

          break;
        }
      }
    }

  };

  text.addListener(SWT.FocusIn, listener);
  text.addListener(SWT.FocusOut, listener);
  text.addListener(SWT.MouseDown, listener);
  text.addListener(SWT.MouseUp, listener);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top