I've got a JFrame
that looks like this:
It's got two JTextField
s on it, one JComboBox
between them and a JPanel
at the bottom (that you can't see).
One of the features of the JComboBox
is that it can be given a custom editor. These implement the ComboBoxEditor
interface. In each of the following three cases, the GUI looks exactly the same, and I would have expected them all to behave exactly the same:
- I do not specify a custom editor, and use the default one.
- I create a custom editor whose editor component is a
JTextField
. - I create a custom editor whose editor component is a
JPanel
with aJTextField
on it (using aBorderLayout
).
When the editor for the editable combo box is set to the default, pressing Tab moves the focus from the top JTextField
into the editing area on the JComboBox
and then into the other JTextField
. If I create a custom editor whose editor component is a JTextField
and otherwise does what you would expect, the same thing happens.
BUT, if I instead create a custom editor whose editor component is a JPanel
with a JTextField
add
ed to it, the focus makes one additional stop. If the focus is on the top JTextField
, then pressing Tab moves the focus to the little arrow at the right of the editable combo box before moving into the text area.
Why is this happening? The focus never moves on to the JPanel
at the bottom of the frame, so why does the presence of a JPanel
holding the JTextField
affect the tab order on the combo box?
The following is an S(-ish)SCCE, which has one text field and all three types of combo box on it:
import javax.swing.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
public class ComboBoxTest extends JFrame
{
private JPanel layoutPanel;
private JTextField meaninglessTextField;
private JComboBox defaultEditorComboBox;
private JComboBox textFieldEditorComboBox;
private JComboBox panelEditorComboBox;
public ComboBoxTest()
{
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
layoutPanel = new JPanel();
layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS));
meaninglessTextField = new JTextField();
defaultEditorComboBox = new JComboBox(); // Just a default JComboBox.
defaultEditorComboBox.setEditable(true);
textFieldEditorComboBox = new JComboBox();
textFieldEditorComboBox.setEditable(true);
textFieldEditorComboBox.setEditor(new TextFieldEditor());
panelEditorComboBox = new JComboBox();
panelEditorComboBox.setEditable(true);
panelEditorComboBox.setEditor(new PanelEditor());
layoutPanel.add(Box.createRigidArea(new Dimension(500,0)));
layoutPanel.add(meaninglessTextField);
layoutPanel.add(defaultEditorComboBox);
layoutPanel.add(textFieldEditorComboBox);
layoutPanel.add(panelEditorComboBox);
Container contentPane = getContentPane();
contentPane.add(layoutPanel, BorderLayout.CENTER);
pack();
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run()
{
new ComboBoxTest().setVisible(true);
}
});
}
private class PanelEditor extends JPanel implements ComboBoxEditor
{
public JTextField inputTextField = new JTextField();
public PanelEditor()
{
setLayout(new BorderLayout());
add(inputTextField, BorderLayout.CENTER);
}
@Override
public String getItem()
{
return inputTextField.getText();
}
@Override
public void setItem(Object newText)
{
if (newText != null) {
inputTextField.setText(newText.toString());
}
else {
inputTextField.setText("");
}
}
@Override
public Component getEditorComponent()
{
return this;
}
@Override
public void removeActionListener(ActionListener listener)
{
inputTextField.removeActionListener(listener);
}
@Override
public void addActionListener(ActionListener listener)
{
inputTextField.addActionListener(listener);
}
@Override
public void selectAll()
{
inputTextField.selectAll();
}
}
private class TextFieldEditor extends PanelEditor implements ComboBoxEditor
{
// The same, except that the editor component is now just the JTextField
// rather than the whole panel.
public TextFieldEditor()
{
}
@Override
public JTextField getEditorComponent()
{
return inputTextField;
}
}
}
Note: this behaviour becomes a problem if I want to add a JLabel
to the editor. Then I have to put a JPanel
there to hold both the label and the text field.