I have a bug lurking where the focus is sporadically not ending up on the text field I want it on after popping up a JOptionPane
containing the text field.
I eventually boiled it down to a reasonable example:
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Component;
public class FocusIssueTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// The real thing has many more components in there,
// but I removed them for the demo.
MyInputPane myInputPane = new MyInputPane();
myInputPane.showDialog(null);
}
});
}
public static class MyInputPane extends JPanel {
private final JTextField textField;
protected MyInputPane() {
textField = new JTextField();
textField.selectAll();
textField.setColumns(30);
setLayout(new BorderLayout());
add(textField, BorderLayout.CENTER);
}
public boolean showDialog(Component parentComponent) {
final JOptionPane optionPane = new JOptionPane(
this, JOptionPane.PLAIN_MESSAGE,
JOptionPane.OK_CANCEL_OPTION);
JDialog dialog = optionPane.createDialog(
parentComponent, "Select a Thing");
/* Attempted solution #1 - wait until the window is active
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(WindowEvent event) {
textField.requestFocusInWindow();
}
});
*/
/* Attempted solution #2 - camickr's RequestFocusListener
textField.addAncestorListener(new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event) {
JComponent component = event.getComponent();
component.requestFocusInWindow();
component.removeAncestorListener(this);
}
@Override
public void ancestorRemoved(AncestorEvent event) {
}
@Override
public void ancestorMoved(AncestorEvent event) {
}
});
*/
/* Attempted solution #3 - HierarchyListener
textField.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent event) {
Component component = event.getComponent();
if ((HierarchyEvent.SHOWING_CHANGED &
event.getChangeFlags()) != 0 &&
component.isShowing()) {
component.requestFocusInWindow();
component.removeHierarchyListener(this);
}
}
});
*/
// Attempted solution #4 - appears to work but can't be
// right, because eww.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
textField.requestFocusInWindow();
}
});
}
});
}
});
dialog.setVisible(true);
Object selectedValue = optionPane.getValue();
return selectedValue instanceof Integer &&
(int) selectedValue == 0;
}
}
}
Attempted solutions #1 through #3 all fail to put the focus in the text field. Attempted solution #4 works but having to use three levels of nested SwingUtilities.invokeLater
calls can't possibly be the proper way to do this.
So what is the proper way?
I notice that JOptionPane.showInputDialog
' text field does receive focus, so clearly there is a way to do it.
I don't know the canonical solution, but you could try just one call to queue on the event thread within your window listener. For e.g.,
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(WindowEvent event) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textField.requestFocusInWindow();
}
});
}
});
Other possible "kludges" include using a short single-run Swing timer.
Have you considered subclassing / initializing a JDialog directly instead of using JOptionPane ?
The reason why java does not have a "setFocusInWindow" is because on some platforms it is not possible to directly "set" the focus (as much as request it. )
To me it seems like the call to "setVisible()" is putting a event on the EDT, to make the window visible, which in turn is changing the focus away from your text field.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With