Reputation: 481
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:
* 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
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