javaswingkeyboard

How to Set JComponent to Traverse with TAB instead of CTRL+TAB without using isManagingFocus()


I have a class that is a custom swing component that extends JComponent. I am trying to make it so that the subcomponents of this custom component will be focus traversable by simply pressing TAB or SHIFT+TAB. Instead, they only seem to be traversable by pressing CTRL+TAB or CTRL+SHIFT+TAB by default no matter what I specify otherwise. I have referred to the answer from here although my component is not a JTextArea, and I have tried setting the focus traverse keys directly with the code below in the constructor, but to no avail.

setFocusCycleRoot(true);
setFocusTraversalKeysEnabled(true);
Set<AWTKeyStroke> forwardKeys = new HashSet<>();
forwardKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);
Set<AWTKeyStroke> backwardKeys = new HashSet<>();
backwardKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK));
setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backwardKeys);

I also tried to add a key listener to the component to effectively do the same thing with the code below, but that also did not work.

addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_TAB) {
            if (e.isShiftDown()) {
                transferFocusBackward();
            } else {
                transferFocus();
            }
            e.consume();
        }
    }
});

I find that I get exactly the behavior I want if I do not include either of the code blocks above, but rather override the isManagingFocus() method to always return true, but I would rather not do this as isManagingFocus() is deprecated.


Solution

  • Yes, I know this question is five years old, and yes, I know that I'm the one who originally submitted it. However, I have returned to this issue and finally found a way to solve it. The main impetus behind this question was to preserve the functionality of a component while eliminating the use of deprecated code. The isManagingFocus() method of JComponent is deprecated, but overriding it to always return "true" caused a component to be focus traversable by TAB and SHIFT+TAB, without the CTRL key, which is the functionality that I wanted to preserve.

    Looking at the source code for the JComponent constructor where isManagingFocus() was called, I found a way to adapt that code in a non-deprecated way in my custom component's constructor that would preserve the same behavior that the component had before. I was surprisingly close in my attempt five years ago, basically missing the additional InputEvent.CTRL_DOWN_MASK modifier when setting the focus keys. Here is what I did:

    Set<KeyStroke> managingFocusForwardTraversalKeys;
    Set<KeyStroke> managingFocusBackwardTraversalKeys;
    synchronized (JComponent.class) {
        managingFocusForwardTraversalKeys = new HashSet<KeyStroke>(1);
        managingFocusForwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK));
        managingFocusBackwardTraversalKeys = new HashSet<KeyStroke>(1);
        managingFocusBackwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK));
    }
    
    //The old deprecated way of doing this that isManagingFocus() would trigger in the JComponent constructor.
    //LookAndFeel.installProperty(this, "focusTraversalKeysForward", managingFocusForwardTraversalKeys);
    //LookAndFeel.installProperty(this, "focusTraversalKeysBackward", managingFocusBackwardTraversalKeys);
            
    //The new way of doing this
    setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, managingFocusForwardTraversalKeys);
    setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, managingFocusBackwardTraversalKeys);