David North
David North

Reputation: 1139

SWT: Differentiating between selection and typing in a combo

Consider the following Java (SWT) code:

private static ComboViewer createViewer(final Shell shell) {
  final ComboViewer v = new ComboViewer(shell, SWT.DROP_DOWN);
  v.setLabelProvider(new LabelProvider());
  v.setContentProvider(new ArrayContentProvider());
  v.setInput(new String[]{"value 1", "value 2"});
  return v;
}

public static void main(final String[] args) {
  Display display = new Display();
  Shell shell = new Shell(display);
  shell.setSize(200, 60); 
  shell.setLayout(new GridLayout());

  final ComboViewer v = createViewer(shell);

  // This wires up the userSelectedSomething method correctly
  v.addSelectionChangedListener(new ISelectionChangedListener() {
    @Override
    public void selectionChanged(final SelectionChangedEvent event) {
      userSelectedSomething();
    }
  });

  shell.open();
  while (!shell.isDisposed()) {
    if (!display.readAndDispatch()) {
      display.sleep();
    }
  }
  display.dispose();
}

public static void userSelectedSomething() {
  // This should be called *only if* the user selected from the drop-down
}

public static void userTypedSomething() {
  // This should be called *only if* the user typed in the combo
}

I want to call the userTypedSomething method only if the user typed into the combo (and not when they selected from the drop-down). What listener should I add to achieve this? Adding a modify listener to the combo viewer with v.getCombo().addModifyListener(...) is no good as this is triggered for both typing and selection from the combo.

Upvotes: 3

Views: 6800

Answers (3)

david.perez
david.perez

Reputation: 7012

A small impovement to @sambi.reddy answer, taking into account not to consider user has typed something when the arrow keys, pg down or pg up are used to select next/previous item:

private static ComboViewer createViewer(final Shell shell) {
    final ComboViewer v = new ComboViewer(shell, SWT.DROP_DOWN);
    v.setLabelProvider(new LabelProvider());
    v.setContentProvider(new ArrayContentProvider());
    v.setInput(new String[]{"value 1", "value 2"});
    return v;
  }

  private static boolean userTyped;
  private static int index = -1;

  public static void main(final String[] args) {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setSize(200, 60); 
    shell.setLayout(new GridLayout());

    final ComboViewer v = createViewer(shell);
    /*
     * invoked multiple times when combo selection happens
     * invoked once when user types
     */
    v.getCombo().addVerifyListener(new VerifyListener() {
      @Override
      public void verifyText(VerifyEvent e) {
         userTyped = if (ev.keyCode != 0 && !List.of(SWT.ARROW_UP, SWT.ARROW_DOWN, SWT.PAGE_DOWN, SWT.PAGE_UP).contains(ev.keyCode));
      }
    });
}

v.getCombo().addModifyListener(new ModifyListener() {

@Override public void modifyText(ModifyEvent e) {

Combo c = (Combo)e.widget;

if(userTyped || index == c.getSelectionIndex() || c.getSelectionIndex() == -1)
{
  userTypedOrEditedSomething();
}
index = c.getSelectionIndex();

} });

// This wires up the userSelectedSomething method correctly
v.addSelectionChangedListener(new ISelectionChangedListener() {
  @Override
  public void selectionChanged(final SelectionChangedEvent event) {
    userSelectedSomething();
  }
});

shell.open();
while (!shell.isDisposed()) {
  if (!display.readAndDispatch()) {
    display.sleep();
  }
}
display.dispose();

}

public static void userSelectedSomething() { // This should be called only if the user selected from the drop-down System.out.println("User selected"); }

public static void userTypedOrEditedSomething() { // This should be called only if the user typed in the combo System.out.println("User typed or edited"); }

Upvotes: 0

sambi reddy
sambi reddy

Reputation: 3085

private static ComboViewer createViewer(final Shell shell) {
    final ComboViewer v = new ComboViewer(shell, SWT.DROP_DOWN);
    v.setLabelProvider(new LabelProvider());
    v.setContentProvider(new ArrayContentProvider());
    v.setInput(new String[]{"value 1", "value 2"});
    return v;
  }

  private static boolean userTyped;
  private static int index = -1;

  public static void main(final String[] args) {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setSize(200, 60); 
    shell.setLayout(new GridLayout());

    final ComboViewer v = createViewer(shell);
    /*
     * invoked multiple times when combo selection happens
     * invoked once when user types
     */
    v.getCombo().addVerifyListener(new VerifyListener() {



      @Override
      public void verifyText(VerifyEvent e) {
         userTyped = (e.keyCode != 0);
      }
    });



 v.getCombo().addModifyListener(new ModifyListener() {

  @Override
  public void modifyText(ModifyEvent e) {

    Combo c = (Combo)e.widget;

    if(userTyped || index == c.getSelectionIndex() || c.getSelectionIndex() == -1)
    {
      userTypedOrEditedSomething();
    }
    index = c.getSelectionIndex();
  }
});

    // This wires up the userSelectedSomething method correctly
    v.addSelectionChangedListener(new ISelectionChangedListener() {
      @Override
      public void selectionChanged(final SelectionChangedEvent event) {
        userSelectedSomething();
      }
    });

    shell.open();
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
    display.dispose();
  }

  public static void userSelectedSomething() {
    // This should be called *only if* the user selected from the drop-down
    System.out.println("User selected");
  }

  public static void userTypedOrEditedSomething() {
    // This should be called *only if* the user typed in the combo
    System.out.println("User typed or edited");
  }

I would suggest you to use Verify event instead Key UP as you might endup handling lot of things (arrow keys, magic keys...etc). Verify is also Key Event but it filter out ALT,CNTRL,SHIFT combination. When user types just check for keycode!=0.

As you pointed out, when you use CNTRL+V ,Right click Menu paste....combo doesn't consider it as key event but it fires verify event to make sure the clipboard text is valid for combo or not. I think this is how it should work as Menu item selection and Key event on combo are different things. you can always monitor all key events for special actions like copy/paste/delete.

the above sample code should be able to perform what you are looking for.

Upvotes: 5

Baz
Baz

Reputation: 36884

Since you want to listen to keyboard input, I would suggest listening to SWT.KeyUp.

This should be a good starting point:

public static void main(String[] args) {
    final Display display = new Display();
    final Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());

    final Combo combo = new Combo(shell, SWT.NONE);

    combo.add("First");
    combo.add("Second");

    combo.addListener(SWT.Selection, new Listener() {

        @Override
        public void handleEvent(Event arg0) {
            System.out.println("Selected: " + combo.getItem(combo.getSelectionIndex()));
        }
    });

    combo.addListener(SWT.KeyUp, new Listener() {

        @Override
        public void handleEvent(Event arg0) {
            System.out.println("Typed");
        }
    });

    shell.pack();
    shell.open();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch()) {
            display.sleep();
        }
    }
    display.dispose();
}

Upvotes: 2

Related Questions