Nimbus often looks great, but for certain color combinations the result is non-optimal. In my case, the background of a JPopupMenu
does not fit, which is why I want to set it manually.
I'm on Java 7 and, interestingly, Nimbus fully ignores the setting of some properties in the UIManager
(like PopupMenu.background
). So my only option was to create a subclass of JPopupMenu
that overrides paintComponent(...)
. I know, that's nasty, but at least it worked.
However, if you add a JMenu
to another menu, it embeds it's own instance of JPopupMenu
and I could not figure out how to replace it with my own subclass.
Even assigning an own PopupMenuUI
to the embedded instance didn't bring any results. If inherited directly from JPopupMenu
the overriden paint(...)
method was called, but, not matter what I did, nothing was drawn. If inherited from javax.swing.plaf.synth.SynthPopupMenuUI
paint
isn't even called and the result is if I hadn't set an own PopupMenuUI
at all.
So the simple question is: How do I adjust the background color of one JPopupMenu
or (if that's easier) all of them on Java 7 using Nimbus as L&F?
Edit: Code example
Take a look at the following code and the result:
public static void main(final String[] args) {
try {
UIManager.setLookAndFeel(NimbusLookAndFeel.class.getCanonicalName());
UIManager.getLookAndFeelDefaults().put("PopupMenu.background", Color.GREEN);
UIManager.getLookAndFeelDefaults().put("Panel.background", Color.RED);
UIManager.getLookAndFeelDefaults().put("List.background", Color.BLUE);
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200,200);
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
JList list = new JList();
panel.add(list);
frame.getContentPane().add(panel);
JPopupMenu menu = new JPopupMenu();
menu.add(new JMenuItem("A"));
menu.add(new JMenuItem("B"));
menu.add(new JMenuItem("C"));
frame.setVisible(true);
menu.show(frame, 50, 50);
}
I know, some say that you should use UIManager.put(key, value)
or UIManager.getLookAndFeelDefautls().put(key,value)
before setting the L&F, but for me this does not bring any results (meaning: no changes to the default colors at all). The code above at least brings:
Same thing (meaning nothing) happens if you use JPopupMenu.setBackground(...)
. This is because Nimbus uses an internal painter, which computes the color from Nimbus' primary colors and ignores the components' property. In this example, you can use the following as workaround:
JPopupMenu menu = new JPopupMenu() {
@Override
public void paintComponent(final Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(0,0,getWidth(), getHeight());
}
};
Which brings
However, this workaround does not work if you insert a JMenu
which itself wraps a JPopupMenu
you can't override:
JMenu jmenu = new JMenu("D");
jmenu.add(new JMenuItem("E"));
menu.add(jmenu);
gives, as expected:
You can retrieve this JPopupMenu
using JMenu.getPopupMenu()
but you can't set it. Even overriding this method in an own subclass of JMenu
does not bring any results, as JMenu
seems to access it's enwrapped instance of JPopupMenu
without using the getter.
One way to do it is to color the background of the individual JMenuItems and make them opaque:
JMenuItem a = new JMenuItem("A");
a.setOpaque(true);
a.setBackground(Color.GREEN);
Then give the menu itself a green border to fill the rest:
menu.setBorder(BorderFactory.createLineBorder(Color.GREEN));
There may be an easy/more straightforward way out there, but this worked for me.