chris
chris

Reputation: 171

PLAF can't change button color

We have a program that can change between "daytime" (high-contrast) and "nighttime" (low-contrast) modes. This is done by changing the look-and-feel on the fly. It works for almost all components, but a few are ignoring the background color changes. In particular, I've noticed buttons, comboboxes, and table headers all ignore the change in the PLAF.

Here is an example program with a button. Am I doing something wrong? Is this an OS-dependent behavior somehow? (I'm on OSX)

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.plaf.ColorUIResource;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class Demo extends JFrame {
   public static void main(String[] args) {
      new Demo();
   }

   public Demo() {
      setTitle("PLAF button test");
      final Demo thisWindow = this;
      final JButton button = new JButton("Click me!");
      button.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            Color curColor = UIManager.getColor("Button.background");
            ColorUIResource targetColor;
            if (curColor.equals(new Color(32, 32, 32))) {
               targetColor = new ColorUIResource(192, 192, 192);
            }
            else {
               targetColor = new ColorUIResource(32, 32, 32);
            }
            System.out.println("Setting new color to " + targetColor +
               " because old color was " + curColor);
            UIManager.put("Button.background", targetColor);
            SwingUtilities.updateComponentTreeUI(thisWindow);
         }
      });
      add(button);
      setMinimumSize(new java.awt.Dimension(200, 200));
      setVisible(true);
   }
}

And here is the output I get when I run this program and click on the button:

Setting new color to javax.swing.plaf.ColorUIResource[r=32,g=32,b=32] because old color was com.apple.laf.AquaImageFactory$SystemColorProxy[r=238,g=238,b=238]

Setting new color to javax.swing.plaf.ColorUIResource[r=192,g=192,b=192] because old color was javax.swing.plaf.ColorUIResource[r=32,g=32,b=32]

Setting new color to javax.swing.plaf.ColorUIResource[r=32,g=32,b=32] because old color was javax.swing.plaf.ColorUIResource[r=192,g=192,b=192]

Setting new color to javax.swing.plaf.ColorUIResource[r=192,g=192,b=192] because old color was javax.swing.plaf.ColorUIResource[r=32,g=32,b=32]

So the UIManager believes the color is changing, but the apparent color on-screen does not change.

Any advice appreciated. Thank you for your time.

Upvotes: 3

Views: 1279

Answers (1)

trashgod
trashgod

Reputation: 205875

Is this an OS-dependent behavior somehow?

The effect is OS-dependent only in the sense that the UI delegate controls the component's appearance, and each supported platform defines a default Look & Feel. On Mac OS X, the default is com.apple.laf.AquaLookAndFeel; here is the corresponding source. As shown in UIManager Defaults, cited here, the UIManager key for "Button.background" has a light-gray value of

com.apple.laf.AquaImageFactory$SystemColorProxy[r=238,g=238,b=238]

but the allotted space is completely covered by the native component. A similar effect obscures the light-pink placeholder shown for "RadioBUtton.icon". Some mitigation strategies:

  • As suggested here, you can set the background of the enclosing JPanel.

  • As suggested here, you can use setOpaque() to tint some of the background; a darker() shade may be helpful.

    JButton b = new JButton("Test");
    b.setOpaque(true);
    b.setBackground(Color.red.darker());
    
  • If you use a Border, remember to "put the component in a JPanel and set the border on the JPanel."

  • Using a control such as the one seen here, let the user choose the preferred Look & Feel; persist the choice in an instance of java.util.prefs.Preferences.

  • Direct the user to the contrast options in the Mac OS X Accessibility System Preferences panel.

  • Wrap platform-specific code in a suitable predicate.

    if (System.getProperty("os.name").startsWith("Mac OS X")) {
        // Specific to Mac OS X
    }
    

Upvotes: 3

Related Questions