I have a requirement to render selected/focused cells in JTable
with a JComboBox
if the column in question uses combo as editor. The intent of this is to give hint to users that cells in the column is edited with a combo rather than a JTextField
.
My problem is that for other LookAndFeels
than Metal it's impossible to set selection background of the table to the combobox. If one click or navigates with the keyboard to a "combo column", the selected cell gets rendered by the combo with the default background.
Metal L&F works as I want it to, but that's not an option since our customers uses either Substance/Nimbus/Windows L&F and it doesn't work for any of them.
Hope I was clear enough, any help is appreciated!
Select cells in "Combo column" of this SSCCE to see what I mean:
import java.awt.Component;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;
public class RenderSelectedCellWithComboSSCCE extends JFrame
{
RenderSelectedCellWithComboSSCCE()
{
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
add(new JScrollPane(createTable()));
pack();
}
private JTable createTable()
{
TableModel model = new DefaultTableModel(
new Object [][] {{"A", "Item 0"},
{"B", "Item 1"},
{"C", "Item 2"},
{"D", "Item 3"},
{"E", "Item 4"}},
new String [] {"TextField", "Combo"});
JTable table = new JTable(model);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
DefaultCellEditor editor = new DefaultCellEditor(new JComboBox(new Object[]{"Item 0", "Item 1", "Item 2", "Item 3", "Item 4"}));
editor.setClickCountToStart(2);
table.getColumnModel().getColumn(1).setCellEditor(editor);
table.getColumnModel().getColumn(1).setCellRenderer(new ComboCellRenderer());
return table;
}
/**
* Renderer that demonstrates problem rendering selected cells with a combo with selection background.
*/
private static class ComboCellRenderer implements TableCellRenderer
{
final TableCellRenderer defaultRenderer;
final JComboBox combo;
ComboCellRenderer()
{
defaultRenderer = new DefaultTableCellRenderer();
combo = new JComboBox();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
if (hasFocus && table != null && table.isCellEditable(row, column))
{
combo.setModel(new DefaultComboBoxModel(new Object[]{value}));
combo.setSelectedItem(value);
combo.setBackground(table.getSelectionBackground()); // This only have desired effect for Metal L&F
return combo;
}
else
{
return defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
setLookAndFeel();
new RenderSelectedCellWithComboSSCCE().setVisible(true);
}
private void setLookAndFeel()
{
try
{
//UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); // Metal works fine
UIManager.setLookAndFeel(new NimbusLookAndFeel());
//UIManager.setLookAndFeel(new WindowsLookAndFeel());
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
//UIManager.setLookAndFeel("org.jvnet.substance.SubstanceLookAndFeel");
}
catch (Exception e)
{
throw new RuntimeException("Failed to set LookAndFeel", e);
}
}
});
}
}
ComboBox rendering does pose problems, typically the LAF has very special ideas about how they want to show the the combo itself, many are styled, have rounded corners ... it's unprobable to it done right for all. While playing a bit with Stani's suggestion I ran into all the nasty (and quickly forgotten :-) details, like
On the other hand, you don't need a fully functional JComboBox, all you need is an visual clue that there is something to open. So you might get away with a custom component - at its simplest a JLabel with some arrows - mocking a JComboBox, something like:
public static class MockCombo extends JLabel {
private JButton arrow;
public MockCombo() {
JComboBox box = new JComboBox();
box.setEditable(true);
arrow = (JButton) box.getComponent(0);
setLayout(new BorderLayout());
add(arrow, BorderLayout.LINE_END);
setOpaque(true);
}
}
Still needs to be fine-tuned by LAF, but a far easier task than tweak a JComboBox