javaswinglook-and-feelsynth

How can I change the "disabled icon" style using SynthLookAndFeel?


I'm currently designing a Swing app with a custom "yellow on black" Look and Feel, and it seems SynthLookAndFeel is the way to go.

All my buttons consist of an ImageIcon (no text), for example: Original

When buttons are disabled, I would like the icons to turn to a faded yellow: Desired

However, the default disabled icons are just greyscale versions of the "enabled" ones, and that breaks the yellow feel: Current

I read in this answer that disabled icons are generated internally by getDisabledIcon(), but I find no place to control it from synth's XML file.

I also read about SynthLookAndFeel's SynthPainter class, but it doesn't seem to address the question of disabled icons.

Is there a way to control that "getDisabledIcon" behaviour using SynthLookAndFeel, or am I asking too much ? In the latter case, what would be the best suited look and feel to use or extend for easy definition of button backgrounds, shapes, etc ?

Any hint is welcome.


Solution

  • OK, I think I found a clean way.

    I was hesitating between finding a way with SynthLookAndFeel or subclassing another L&F... But didn't think of subclassing SynthLookAndFeel itself :-)

    I've now got an implementation of SynthLookAndFeel that does exactly what I want, meaning the "disabled" icon is not a greyscale one, it's a desaturated, dimmed, color version: enter image description here

    Here we go for the full code:

    import javax.swing.*;
    import javax.swing.plaf.synth.SynthLookAndFeel;
    import java.awt.*;
    import java.awt.image.FilteredImageSource;
    import java.awt.image.ImageProducer;
    import java.awt.image.RGBImageFilter;
    
    public class MySynthLookAndFeel extends SynthLookAndFeel {
        @Override
        public Icon getDisabledIcon(JComponent component, Icon icon) {
            if (icon instanceof ImageIcon) {
                return new ImageIcon(createDisabledImage(((ImageIcon)icon).getImage()));
            }
            return null;
        }
    
        private static Image createDisabledImage(Image i) {
            ImageProducer prod = new FilteredImageSource(i.getSource(), new RGBImageFilter() {
                public int filterRGB(int x, int y, int rgb) {
                    // extract alpha mask
                    int alphamask = rgb & 0xFF000000;
    
                    // convert to HSB
                    float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff, null);
                    // desaturate (half saturation)
                    hsb[1] *= 0.5;
                    // dim (half brightness)
                    hsb[2] *= 0.5;
                    // convert back to RGB
                    int rgbval = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
    
                    // reapply alpha
                    rgbval = rgbval & 0x00FFFFFF | alphamask;
                    return rgbval;
                }
            });
            return Toolkit.getDefaultToolkit().createImage(prod);
        }
    }
    

    Much simpler than I thought, in the end.