I am trying to create a combo box so that I can put whatever control I prefer within the pop-up, in my specific case a JTree. Having a look at how the JComboBox is implement, the pop-up is really created by the UI delegate. The problem in changing that is that it would need to be re-implemented for each look and feel, which is something I do not want to do...
I basically want a component that it has the look and feel of a JComboBox (in the current look and feel) and the popup is a JTree (in the current look and feel).
What's the easiest way to do that?
JComboBox itself can't do what you want. If you're absolutely wedded to the concept of having it act like a JComboBox, you could make a JButton pop up a JPanel on click. Then the JPanel could have whatever you want inside it (JTree, etcetera).
I've had a go at producing something that would do something like this.
At first, I tried to implement something along the lines suggested by Varun, but it was proving to be a bit messy, and I get a little nervous when I start playing with ComponentUI objects (I'd rather leave that sort of thing to the L&F). If anyone has a good example of doing this, I'd be interested to see it.
So then I tried the button approach... and thought I would share the code with the SO community:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.plaf.basic.BasicInternalFrameUI;
import javax.swing.plaf.metal.MetalComboBoxIcon;
public class MockJComboBox extends JPanel {
private boolean _isPoppedUp = false;
public MockJComboBox(String label, final JComponent toShow) {
setLayout(new BorderLayout());
JLabel jLabel = new JLabel(label);
jLabel.setBackground(Color.WHITE);
add(jLabel, BorderLayout.CENTER);
Icon icon = new MetalComboBoxIcon();
final JInternalFrame popup = new JInternalFrame(null, false, false, false, false);
final JPanel panel = new JPanel(new BorderLayout());
panel.add(new JScrollPane(toShow), BorderLayout.CENTER);
if(!(System.getProperty("os.name").startsWith("Mac OS"))){
BasicInternalFrameUI ui = (BasicInternalFrameUI) popup.getUI();
ui.getNorthPane().setPreferredSize(new Dimension(0,0));
}
popup.setBorder(null);
popup.setContentPane(panel);
popup.pack();
popup.setVisible(true);
final JButton dropDownButton = new JButton(icon);
dropDownButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
_isPoppedUp = !_isPoppedUp;
Container parent = getParent();
if (_isPoppedUp) {
popup.setLocation(panel.getX(), panel.getY() + panel.getHeight());
popup.setSize(panel.getWidth(), toShow.getHeight());
parent.add(popup);
} else {
parent.remove(popup);
parent.repaint();
}
}
});
add(dropDownButton, BorderLayout.EAST);
}
public boolean isPoppedUp() {
return _isPoppedUp;
}
}
If you spot any bugs or have suggestions on how to improve this code, I'd be grateful for your comments!
You just need to extend the BasicComboBoxUI and then override required methods like
public static ComponentUI createUI( JComponent c)
and
protected ComboPopup createPopup()
Creating a custom ComboPopup would require you to put some effort where you can't use the BasicComboPopUp because it extends JPopUpMenu
public class BasicComboPopup extends JPopupMenu implements ComboPopup
So in your case you may want to extend JTree and implement ComboPopup.
I'm doubt about "The problem in changing that is that it would need to be re-implemented for each look and feel" part. I don't think that there will be a problem of re-implementation.
The BasicComboPopup looks differently in different look and feels because it is a JPopupMenu which in turn will have UI delegates. So if you simply extend a JTree you should not be having problems with different look and feels.
The answer to use a button that pops up a JPanel with a JTree is correct. In response to Carcassi's comment, you can use a custom TableCellRenderer to change it so that it does not look like the traditional button.
Further web research revealed that Jidesoft, who describe themselves as "a professional Java and Swing component provider" produce a package called JIDE Grids, which includes AbstractComboBox - the description for which suggests it would do this.
However, it's a paid-for package and I haven't tried it... If anyone has used this, could they comment on the experience?