I have creeated a custom cell renderer for JComboBoxes, based on DefaultListCellRenderer (my one basically shows a color). So far it works nicely.
The only problem is, when the JComboBox is disabled: In this case, I should also paint the color in a less shiny way (to show it is inactive). But how do I get the status in the ListCellRenderer ?
I tried isEnabled() or component.isEnabled(), but this does not seem accessible / give me the actual state of the Combo. In the DefaultListCellRenderer, there is a query to list.isEnabled(), but that does not make sence to me (and it does not work, either).
Any ideas?
A little bit tricky but possible ;)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
/**
* <code>ComboTest</code>.
*/
public class ComboTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ComboTest()::startUp);
}
private void startUp() {
JFrame frm = new JFrame("Combo test");
JComboBox<String> combo = new JComboBox<>(new String[] {"One", "Two", "Three"});
combo.setRenderer(new EnablementCellRenderer());
JButton b = new JButton("Toggle Enabled");
b.addActionListener(l -> {
combo.setEnabled(!combo.isEnabled());
combo.repaint();
});
frm.add(combo);
frm.add(b, BorderLayout.EAST);
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
private class EnablementCellRenderer extends BasicComboBoxRenderer {
@Override
protected void paintComponent(Graphics g) {
JComboBox<?> combo = getFirstAncestorOfClass(this, JComboBox.class);
setForeground(combo.isEnabled() ? Color.BLUE : Color.GREEN);
super.paintComponent(g);
}
}
/**
* Searches for the first ancestor of the given component which is the instance of the given class.
*
* @param aStart start component to search. If the component is instance of the class - it will be returned.
* @param condition condition used to determine the component.
* @return first ancestor of the given component which is the instance of the given class. Null if no such component found.
*/
@Nullable
public static Container getFirstAncestor(Component aStart, Predicate<Component> condition) {
Container result = null;
Component base = aStart;
while ((result == null) && (base.getParent() != null || getInvoker(base) != null)) {
base = getInvoker(base) == null ? base.getParent() : getInvoker(base);
result = condition.test(base) ? (Container) base : null;
}
return result;
}
/**
* Searches for the first ancestor of the given component which is the instance of the given class.
*
* @param aStart start component to search. If the component is instance of the class - it will be returned.
* @param aClass class of component.
* @return first ancestor of the given component which is the instance of the given class. Null if no such component found.
* @param <E> class of component.
*/
@Nullable
public static <E> E getFirstAncestorOfClass(Component aStart, Class<E> aClass) {
return aClass.cast(getFirstAncestor(aStart, aClass::isInstance));
}
/**
* Gets the invoker of the given component when it's a pop-up menu.
*
* @param c component which invoker must be found.
* @return the invoker when the given component is a pop-up menu or null otherwise.
*/
private static Component getInvoker(Component c) {
return c instanceof JPopupMenu ? ((JPopupMenu) c).getInvoker() : null;
}
}