Search code examples
javaswingjcomboboxkey-bindings

How to detect TAB key-press in JComboBox?


I am trying to find out how to detect if a JComboBox lost focus when user pressed tab or via a mouse-click outside the component's area.

Adding a FocusListener to the editor component of the JComboBox does not help me, as I can't find out if user used the mouse or moved the focus via the tab key. Any ideas would be greatly appreciated.

Edit 1: What I am trying to achieve is this:

  • Suppose user drops down the list (JComboBox popupMenu shows up), and navigates via cursor keys...
  • Case 1: user presses tab. In this case, I want to cut off some of the information from the item and show only some parts.
  • Case 2: user clicks with the mouse outside the popupMenu's area (here we have subcases, but they all fall into the same category). In this case I want to change JComboBox to show the previously edited item, not what user navigated...
  • There are several other cases (mouse item pick, the enter key, escape, etc.). I could easily handle these, but detecting tab is tricky because I can't catch this event as it is handled by FocusManager.

Edit 2: It seems that I have to use setFocusTraversalKeysEnabled(false) to get notified when TAB is pressed, and when i capture that event, I should manually transfer focus... I do not like this solution, but that is so far the best I could come up with.

Solution:

Following piece of Java code is actually solving my problem. As I wrote in Edit 2 the easiest solution was to disable focus traversal. I shamelessly borrowed Kleopatra's code, and all works now. :)

    if (!isTableCellEditor()) {
        comboBoxEditor.setFocusTraversalKeysEnabled(false);

        Action myAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                handleTabPress();
                comboBoxEditor.transferFocus();
            } // actionPerformed() method
        };

        comboBoxEditor.getActionMap().put("tab-action", myAction);
        comboBoxEditor.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
            .put(KeyStroke.getKeyStroke("TAB"), "tab-action");
    } // if

Thanks to all participants in the discussion!


Solution

  • As I understand your question, there are two separate problems

    • don't commit the value while navigating
    • take over the reaction to TAB

    if so, the answers are

    • configure the combo so that it thinks it is the editing component of a CellEditor
    • disable the default traversal keys for the combo and take over with a custom binding

    In code:

        final JComboBox simpleBox = new JComboBox(Locale.getAvailableLocales());
        // this line configures the combo to only commit on ENTER 
        // or selecting an item from the list
        simpleBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
        //
        // simpleBox.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
        //     Collections.EMPTY_SET);
        // just noticed the OPs edit - following indeed is easier to disable _all_ traversal
        // keys with one statement
        simpleBox.setFocusTraversalKeysEnabled(false);
    
        Action myAction = new AbstractAction() {
    
            @Override
            public void actionPerformed(ActionEvent e) {
                LOG.info("got it!");
                simpleBox.transferFocus();
            }
    
        };
        simpleBox.getActionMap().put("tab-action", myAction);
        simpleBox.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
           .put(KeyStroke.getKeyStroke("TAB"), "tab-action");