recluse
recluse

Reputation: 481

Java Swing how to let the JFrame gain focus

I'm developing a java swing application. I wish to achieve such result: press shortcut to let the app window show up. press shortcut again or click otherwhere to let the app hide.

I use jkeymaster to register to listen the event of global shortcut and it works well. But when the user focus on some other window, like chrome, office... the focus window will be other application. Then if the user use the shortcut. My app windows will still show up but cannot gain focus. Could anyone help me to solve it. Here's my code snippet.

    Provider provider = Provider.getCurrentProvider(true);
    // bind shortcut
    provider.register(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), x -> {
        homePage.setVisible(!homePage.isVisible());
        if(homePage.isVisible()){
            // TODO: 2020/10/1 request focus here
            homePage.requestFocusInWindow();
            homePage.requestFocus();

            KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
            focusManager.clearGlobalFocusOwner();

            log.info("hasFocus? " + homePage.hasFocus());
            log.info("isActive? " + homePage.isActive());
        }
    });

What I have tried:

  1. requestFocus and requestFocusInWindow, doesn't work
  2. I read the source code of requestFocus. It says
     * This method cannot be used to set the focus owner to no Component at
     * all. Use <code>KeyboardFocusManager.clearGlobalFocusOwner()</code>
     * instead.

So I tried focusManager.clearGlobalFocusOwner() but it still doesn't work.

Upvotes: 5

Views: 1829

Answers (1)

George Z.
George Z.

Reputation: 6808

Usually when you want to gain focus in a parent container (such as JPanel, JFrame, etc) you request the focus by calling requestFocusInWindow() method into one of its children.

I suggest you, to not asking for focus in the JFrame. Call requestFocusInWindow() in one of its components.

Now in terms of UX, I would give the focus back to last component that was focused. In order to get the focused component of a JFrame we call jframe.getFocusOwner(). However, if the JFrame is in state MINIMIZED or is in background, since it does not have focus, getFocusOwner returns null.

So, in order to find the last component that was focused before calling your global key listener, you can register a global AWT listener for focus events as well:

Toolkit.getDefaultToolkit().addAWTEventListener(e -> {
    if (e.getID() == FocusEvent.FOCUS_LOST) {
        if (e.getSource() instanceof Component) {
            lastFocusedComponent = (Component) e.getSource();
        }
    }
}, FocusEvent.FOCUS_EVENT_MASK);

Listener will be fired every time the focus changes. So you get the actual last component with focus safely, eliminating the chance to be null since it is the one that fires the event.

All you have to do after it, is to call requestFocusInWindow to it:

provider.register(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), e -> {
    frame.setVisible(!frame.isVisible());
    if (frame.isVisible())
        SwingUtilities.invokeLater(lastFocusedComponent::requestFocusInWindow);
});

A full example that works (you can confirm your self that the focus is restored):

public class FocusExample {
    private static Component lastFocusedComponent;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {

            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            JTextField leftField = new JTextField(10);
            JTextField rightField = new JTextField(10);

            frame.setLayout(new FlowLayout());
            frame.add(leftField);
            frame.add(rightField);

            Toolkit.getDefaultToolkit().addAWTEventListener(e -> {
                if (e.getID() == FocusEvent.FOCUS_LOST) {
                    if (e.getSource() instanceof Component) {
                        lastFocusedComponent = (Component) e.getSource();
                    }
                }
            }, FocusEvent.FOCUS_EVENT_MASK);

            Provider provider = Provider.getCurrentProvider(true);

            provider.register(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), e -> {
                frame.setVisible(!frame.isVisible());
                if (frame.isVisible())
                    SwingUtilities.invokeLater(lastFocusedComponent::requestFocusInWindow);
            });

            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });

    }
}

Upvotes: 4

Related Questions