javaswingjcomboboxlistcellrenderer

JComboBox custom cell renderer -how to get isEnabled() state of Combo?


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?


Solution

  • 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;
        }
    
    }