Nimbus UIManager UIDefaults

Nimbus is completely configured by properties in the UIManager defaults table. In my last blog I showed a simple example of how to skin a single component. This gave you a sneak peek into the power of these properties. Lots of people have asked for a complete list of properties that can be set, well its simple just grab UIManager.getLookAndFeelDefaults() and iterate over the contents of the map and print the keys and values and there you go. Well I am not that cruel so I did the work for you and will include a link to a complete table of them at the end. But before I get to that let me explain how they work.

Nimbus properties follow some rules and you can not only use the property keys that we have added in as default but you can create your own. Nimbus Look and Feel scans the UIDefaults table when ever you add a property with UIManager.put(<<key>>,<<value>>) then updates its internal state. When that new property matches the current state of the component it applies to then it will be applied to that component. Properties cascade like CSS and the most specific matching one is used.
Example:

  • foreground = Color.BLACK
  • Label.foreground = Color.BLUE
  • Label[Disabled].foreground = Color.GRAY
  • "SomeLabel"[Disabled].foreground = Color.WHITE

In these examples foreground applies to the foreground of all regions of all components, Label.foreground applies to only Label components, Label[Disabled].foreground applies to only Label components in the Disabled state. "SomeLabel"[Disabled].foreground applies to all components named “SomeLabel” that are in the disabled state. Hopefully that also explains the different ways you can write a rule to match a Component. The only 2 cases that are not covered here are Component.Region.foreground which lets you specify a sub-region of a component and ComponentA:ChildComponentB.foreground which lets you specify a ChildComponentB contained within ComponentA. You can see many examples of these in the complete table below and then play with writing your own. For example you can use the “name” property of a component in a similar way to “class” in CSS, say naming a bunch of buttons “hotButton” and then specifying new rules for them in the defaults table. I already explained in my last blog how you can override the global defaults on a single instance bases which matches the “style” attribute with HTML CSS.

Colors in Nimbus are derived, which means there are a core set of colors which are constants and all the other colors are calculated from those. This means you can simply change those and the 1000s of other colors that are related and used in the painters will update to reflect the new base color. These colors are shown in the “Primary Colors” section of the table. The colors in the “Secondary Colors” section are derived from those in the “Primary Colors” section but themselves are used as the base colors for other colors. You may need to change the secondary colors to tweak the results of changing the primary ones if you are not happy with the results. You can you derived colors in your own code as well so that you colors can change when the primary colors change. This will allow you to make your application color theme-able idea for white label branding etc. The method you need is on NimbusLookAndFeel called getDerivedColor :

/**
 * Get a derived color, derived colors are shared instances and is color
 * value will change when its parent UIDefault color changes.
 *
 * @param uiDefaultParentName The parent UIDefault key
 * @param hOffset             The hue offset
 * @param sOffset             The saturation offset
 * @param bOffset             The brightness offset
 * @param aOffset             The alpha offset
 * @param uiResource          True if the derived color should be
 *                            UIResource, false if it should not be
 * @return The stored derived color
 */
public Color getDerivedColor(String uiDefaultParentName,
               float hOffset, float sOffset, float bOffset, int aOffset,
               boolean uiResource)

Hopefully the rest will make sense as you read though the table and look at all the examples. After that its just a matter of try playing with some and see what you can do. I hope you see that power in this that Richard Bair and I designed and hoped would be useful. The complete defaults properties table is way to big to include in this post so I have put it on a separate page.

If you would like to see how I made this list, here is the code that creates the html.

Java Icon
NimbusBrowser.java
 

30 Responses to “Nimbus UIManager UIDefaults”

  1. Matt Nathan Says:

    Wow, that’s really useful. I like the way you have ‘intelligent’ keys for your UI defaults, very intuitive, also nice presentation of the values for the properties. It also makes it nice and easy to see where a painter may not scale particularly well (FileChooser[Enabled].upFolderIconPainter for example) or at all (Spinner:”Spinner.nextButton”[Disabled].foregroundPainter).

    What is the policy of adding additional Component.States? for example if you wanted to add a UnfocusedWindow state to say the menu bar?

    Also, I think it’s “the quick brown fox jumps over the [i]lazy[/i] dog” ;-)

  2. Tom Says:

    Exactly what I was looking for, thanks a million!

  3. Eric Burke Says:

    This is perfect for what I’m doing right now. This is already an invaluable resource, thank you so much for posting this.

  4. hLk Says:

    I get this error with java 1.6.0_10rc:
    Exception in thread “AWT-EventQueue-0″ java.lang.ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.String
    at NimbusBrowser$1.run(NimbusBrowser.java:81)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

    Quick fix is tu replace line 81 with:

    String key = null;
    if (keyObj instanceof String) {
    key = (String) keyObj;
    } else if (keyObj instanceof StringBuffer) {
    key = ((StringBuffer) keyObj).toString();
    } else {
    throw new IllegalArgumentException();
    }

  5. Sgt Garcia Says:

    How do you set properties for JTextField, JPasswordField… when the “editable” property is set to false (read only).

  6. Robert Lebel Says:

    Very useful, thank you very much for this !

  7. Stig Says:

    I cannot get any styling to work. Should’nt

    UIManager.put(”Panel.background”, new Color(255,255,255));

    give me white panels? It will not work with Nimbus but works fine with Widnows or Metal.

  8. emzic Says:

    hello, i can confirm the problems posted by hLk and Stig.

    i can neither get any skinning to work nor does the provided example compile on jdk6u10rc2.

  9. Jasper Potts Says:

    Thanks guys for the info on 6u10 RC2 I will try and find out what happend, how it got broken :-(

  10. Jasper Potts Says:

    I am still tracking down the issue but I have a workaround. You just need to swap the order of the two lines:

    slider.putClientProperty(”Nimbus.Overrides”,sliderDefaults);
    slider.putClientProperty(”Nimbus.Overrides.InheritDefaults”,false);
    to
    slider.putClientProperty(”Nimbus.Overrides.InheritDefaults”,false);
    slider.putClientProperty(”Nimbus.Overrides”,sliderDefaults);

    That should fix it for now. I just can’t work out how this could have broken as there were no code changes between RC and RC2 any where near Nimbus.

  11. Iain Milne Says:

    It seems to be broken for me too, but in RC1. I must be missing something in your reply about the slider.putClientProperty() - how does that relate to wanting to perform UIManager.put(”background”, Color.whatever…) ?

  12. Mats Says:

    Even with that fix there is tons of stuff that do not seem to work. For instance, changing the selection color of a JComboBox (in the drop down list). I have tried every possible key and it does not change… And I am fairly certain I am doing things right as am doing the exact same thing as is being done in the NimbusDefaults class.

    Changing the selection color does not work even if I change the actual nimbus defaults: plaf.getDefaults().put()

  13. Mats Says:

    Turns out you have to change the “List[Selected].textBackground” to change the selection color of the JComboBox drop down list… That did not make sense to me.

  14. Andre Says:

    I run into the same problems as emzic. Updating the UIManager with some primary and secondary colors does work, but setting a panel’s background to white (as mentioned above) doesn’t change anything. Help, please!

  15. Jasper Potts Says:

    Stig, Andre, emzi: There is 2 parts to that issue first is you need [Enabled] in there otherwise the default value which has Panel[Enabled].background is considered a better match and replaces Panel.background. So if you have the line:

    UIManager.put(”Panel[Enabled].background”, Color.BLUE);

    Then that will work, almost it now comes the second issue. We are caching the values read from UIDefaults which was critical for performance and they are not being cleared at the moment. I am trying to find a solution to this bit. If you put the UIManager.put(”Panel[Enabled].background”, Color.BLUE); line before creating any Swing component then it will work. Any later then it will be cached and not read again. Hope that helps for now and I will try and find a better answer.

  16. Andre Says:

    Jasper, thanks alot for the quick reply. Much appreciated!

    I wonder .. there is no key “Panel[Enabled].background” in the “Nimbus UIDefaults Properties List” that you link to. Does this mean that whenever some key doesn’t seem to work, we should add something like [Enabled] or [Disabled] to the key? If yes, this very Properties List is a little irritating as it sometimes mentions the “states” and sometimes doesn’t.

  17. Iain Milne Says:

    Sorry to bug you again Jasper, but could you help out with the following:

    // Works
    UIManager.put(”Panel[Enabled].background”, new Color(240, 240, 240));
    // Doesn’t work
    UIManager.put(”background”, new Color(240, 240, 240));
    // Doesn’t work
    UIManager.put(”menu”, new Color(255, 255, 255));
    // Works
    UIManager.put(”nimbusOrange”, new Color(51,98,140));
    // Doesn’t work
    UIManager.put(”List.disabled”, new Color(255, 255, 255));

    In my case, I’m basically trying to make the scheme look a little less gray. Ideally, I’d like to be able to set a single background color for panels/windows/etc. The other tweaks are to get a Windows-style white menu background and to reduce the “woah” effect on the orange progress bars that most people seem to have :-)

    I’d also like to globally shrink the font size too but the following only seems to work on a couple of components:
    UIManager.put(”font”, new Font(—whatever—));

  18. Jasper Potts Says:

    Iain: The issue with some of the UIManager defaults like “background” in fact most of the short simple names without states are just place holders for backwards compatibility with BasicUI, they are not used by Nimbus painters. Also Nimbus always uses the most specific rule so always look for the longest most exact rule that matches what you are after and that will most likely be what you need.

    On the font issue try the “defaultfont” key but make sure you set it early like before setting LAF to Nimbus.

  19. Paul Blessing Says:

    Am I missing them somewhere, or are there no keys to allow setting a backgroundPainter on a ToolBar:Button other than [Focused+MouseOver], [Focused+Pressed], [Focused], [MouseOver], and [Pressed]? I’m wanting to be able to set a backgroundPainter for a toolbar button in its other states as well.

  20. Christian Says:

    Jasper, thats really good work.
    I am currently thinking of adapt Nimbus to be the base LAF for my companies applications. (currently jgoodies plasticxp)
    But I have one more specific requirement. We use our LAF implementation to let the application grow to fit touchscreen requirements. So I need to change the default preferred size behaviour for textfields, buttons etc dynamically and on the fly.
    How would that be possible using Nimbus? any uimanager properties for that?

  21. Stefan Gürtler Says:

    I have found an issue: Create a context Menu with an icon and a sub-menu. The icon and text in context menu is not aligned to the right side of the context menu panel but to the left. The arrow for opening the submenu therefore is placed over the label of the menu entry of the submenu. Judging from the space in-between the left border of the context menu panel and the start of the menu item label on one side and the space between the arrow and the right border of the context menu panel (They seem to equal), I would guess there is an alignment issues!

    Expected layout of context menu:

    |Sub menu label |> |

    Actual layout of context menu:

    | Sub menu |>el|

    ‘|>’ indicates the position of the arrow.

    This issues was observed on both windows and linux.

  22. Stefan Gürtler Says:

    Correction:
    The icon and text in context menu is not aligned to the LEFT side of the context menu panel but to the RIGHT.

  23. Jasper Potts Says:

    Stefan: Can you file a bug for that on the Sun site and we will look into fixing it in the next round of Nimbus bug fixing

  24. Tbee Says:

    I’m having some troubles with Nimbus and TableCellRenderers.

    Nimbus uses a black-on-white for regular, but a white-on-blue for selected cells. My cell renderer and editor consist of a panel where two jtextfields and two buttons are present. One textfield is editable. How do I redirect the color info to the editable textfield, so it foreground is white when it is selected?

  25. Tbee Says:

    And the automatic margin and gaps that Nimbus adds, also are a problem. Nimbus surely isn’t a drop-in replacement for existing layouts. For example: stand alone images that can be clicked upon (an ImageButton) which are renderer by using JButton with all paints turned off (border, background, etc) still have these huge margins. So I have written a custom ImageButton extending JLabel instead of JButton.

    And it even gets worse when these components are used in JTables. I have to set the row height to 30 pixels in order to show a JButton with a 16×16 icon in it (to be precise: I added a “autosize rows” feature to my JTable, so this is handled automatically). Maybe it would be wise to reduce these margins when a component is used as a cellrenderer/editor.

  26. Tbee Says:

    Oh, other example: I have a screen which does some precies layouting, including gaps and margins using MigLayout. When Nimbus shows this screen, there are all kinds of extra margins which basicaly voids the margins done in the screen.

    I understand what Nimbus tries to do with the automatically adding of whitespace to screens, but it should be optional.

  27. Jethro Borsje Says:

    Nice post, thank you for the info.

    However, I found a few problems:
    [1] - Label.foreground does not work, but Label.textForeground does the trick
    [2] - I can not seem to get the “SomeLabel”.textForeground example working. This is my code:
    [code]package swing.nimbus;

    import java.awt.Color;
    import java.awt.GridLayout;

    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.border.EmptyBorder;

    import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;

    public class NimbusUIDefaultsTest
    {
    public static void main(String[] args) throws UnsupportedLookAndFeelException
    {
    NimbusLookAndFeel laf = new NimbusLookAndFeel();
    laf.getDefaults().put(”\”SomeLabel\”.textForeground”, Color.BLUE);

    UIManager.setLookAndFeel(laf);

    // Create the frame and show it.
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(buildPanel());
    frame.pack();
    frame.setResizable(false);
    frame.setVisible(true);
    }

    private static JPanel buildPanel()
    {
    JLabel lbl = new JLabel(”Enter name here:”);
    lbl.setName(”SomeLabel”);
    JTextField field = new JTextField();
    field.setColumns(10);
    JButton valid = new JButton(”Valid”);
    JButton invalid = new JButton(”Invalid”);

    JPanel retval = new JPanel(new GridLayout(2, 2));
    retval.add(lbl);
    retval.add(field);
    retval.add(valid);
    retval.add(invalid);
    retval.setBorder(new EmptyBorder(4, 4, 4, 4));

    return retval;
    }
    }[/code]

    what am I doing wrong?

  28. Olivier Gout Says:

    Thanks a lot !
    It’s very useful for the Nimbus theme tool I am working on.

  29. Mark Says:

    Nimbus is looking awesome. Just remind management that the last 10% of development takes the other 50% of effort and not to stretch it out over so many years that nobody cares anymore.
    My initial hurdle with deploying Nimbus would appear to involve the caching mechanism described in prior posts. For the next release of one of our products, we would like to have the user be able to increase or decrease the font size. The natural way to do this appears to be through the UIManager with something like this:

    public void actionPerformed(ActionEvent e) {
    try{
    UIManager.put(”Label.font”, new FontUIResource(theNewFont));
    SwingUtilities.updateComponentTree(myAppFrame);
    } catch (Throwable t) {
    t.printStackTrace();
    }

    }
    This doesn’t work in Nimbus. Is there another approach we should look at?

  30. Jasper Potts Says:

    Mark: You can set the default font for the Nimbus Look and Feel with the UIManager key “defaultFont” this will work if set before loading the look and feel. I have not tested live switching of the font and it will take some more experimenting to find a answer to that. I would have expected the Label.font to work as well not sure what is broken there. Will look into it when I get time.

Leave a Reply