So, here is what I got so far, but one major problem is the problems with cross L & F. An alternative would be to iterate over all the values โโof the ComboBox model and the value "No choice" and check which one is the longest. Then I could set it as prototypeDisplayValue. The problem is that I need a graphical context to measure the boundaries of each line.
Here are two solutions we found out with @Enwired and @Robin. Thanks to both of them.
EDIT: after discussing with @Robin, I found out that this solution is actually much simpler and works on all platforms and looks and feels. The only drawback is that we need to create an additional JComboBox.
import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagLayout; import javax.swing.DefaultListCellRenderer; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.UnsupportedLookAndFeelException; public class TestComboBox { protected void initUI() { JFrame frame = new JFrame(TestComboBox.class.getSimpleName()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(new GridBagLayout()); JComboBox comboBox = new JComboBox(new Object[] { "Something", "Stuff", "Beep" }) { private JComboBox internal; private JComboBox getInternalComboBox() { if (internal == null) { internal = new JComboBox(new Object[] { null }); } return internal; } @Override public Dimension getPreferredSize() { Dimension preferredSize = super.getPreferredSize(); if (getSelectedItem() == null) { getInternalComboBox().setRenderer(getRenderer()); Dimension nullDimension = getInternalComboBox().getPreferredSize(); preferredSize.width = Math.max(preferredSize.width, nullDimension.width); preferredSize.height = Math.max(preferredSize.height, nullDimension.height); } return preferredSize; } @Override public void updateUI() { internal = null; super.updateUI(); } }; comboBox.setRenderer(new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value == null) { setText("No selection"); } return comp; } }); comboBox.setSelectedItem(null); panel.add(comboBox); frame.add(panel); frame.setSize(200, 100); frame.setVisible(true); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new TestComboBox().initUI(); } }); } }
EDIT 2: after discussion with @Enwired, this alternative solution came about to directly override ListCellRenderer getPreferredSize. In this case, you can try to go through the various available L & Fs.
import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultListCellRenderer; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.UnsupportedLookAndFeelException; public class TestComboBox { protected void initUI() { final JFrame frame = new JFrame(TestComboBox.class.getSimpleName()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(new GridBagLayout()); JComboBox comboBox = new JComboBox(new Object[] { "Something", "Stuff", "Beep" }); comboBox.setRenderer(new DefaultListCellRenderer() { private Dimension nullDimesion; @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if (value != null && nullDimesion == null) { nullDimesion = ((JComponent) getListCellRendererComponent(list, null, -1, false, false)).getPreferredSize(); } Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value == null) { setText("No selection"); } return comp; } @Override public Dimension getPreferredSize() { Dimension preferredSize = super.getPreferredSize(); if (nullDimesion != null) { preferredSize.width = Math.max(preferredSize.width, nullDimesion.width); preferredSize.height = Math.max(preferredSize.height, nullDimesion.height); } return preferredSize; } @Override public void updateUI() { nullDimesion = null; super.updateUI(); } }); comboBox.setSelectedItem(null); final JComboBox uiComboBox = new JComboBox(UIManager.getInstalledLookAndFeels()); uiComboBox.setRenderer(new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof LookAndFeelInfo) { LookAndFeelInfo info = (LookAndFeelInfo) value; setText(info.getName()); } return comp; } }); uiComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(((LookAndFeelInfo) uiComboBox.getSelectedItem()).getClassName()); SwingUtilities.updateComponentTreeUI(frame); } catch (ClassNotFoundException e1) {