We are working on a Swing application with Nimbus LaF. We have changed many of the Nimbus defaults (control, text, NimbusLightBackground and so on) to have a dark theme.
Now we have great trouble with the rendering of JLists and JComboBoxes, because the renderer apparently uses the NimbusLightBackground color for the selected text foreground. This results in dark grey text on dark blue background - not good.
I have tried overriding any applicable-seeming key in Nimbus Defaults ("ComboBox:\"ComboBox.listRenderer\"[Selected].textForeground" and suchlike) both globally via UIManager.putDefault() and per-component-overrides, but simply cannot get any change.
Even SwingX Highlighters can't seem to override this behaviour, at least in the combobox dropdown.
Any ideas on how to set the selected text foreground color for J(X)List and J(X)ComboBox Dropdowns?
My latest attempt at per-component-override:
JXComboBox comboBox = new JXComboBox();
UIDefaults comboBoxTheme = new UIDefaults();
comboBoxTheme.put("nimbusLightBackground", new Color(0xFFFAFA));
comboBoxTheme.put("ComboBox:\"ComboBox.listRenderer\"[Selected].textForeground", new Color(0xFFFAFA));
comboBox.putClientProperty("Nimbus.Overrides.InheritDefaults", true);
comboBox.putClientProperty("Nimbus.Overrides", comboBoxTheme);
SwingUtilities.updateComponentTreeUI(comboBox);
And the application-wide nimbus defaults:
ColorUIResource backgroundUI = new ColorUIResource(0x494949);
ColorUIResource textUI = new ColorUIResource(0xFFFAFA);
ColorUIResource controlBackgroundUI = new ColorUIResource(0x5F5F4D);
ColorUIResource infoBackgroundUI = new ColorUIResource(0x2f5cb4);
ColorUIResource infoUI = new ColorUIResource(0x2f5cb4);
ColorUIResource lightBackgroundUI = new ColorUIResource(0x5D5D5B);
ColorUIResource focusUI = new ColorUIResource(0x39698a);
UIManager.put("control", backgroundUI);
UIManager.put("text", textUI);
UIManager.put("nimbusLightBackground", lightBackgroundUI);
UIManager.put("info", infoUI);
UIManager.put("nimbusInfoBlue", infoBackgroundUI);
UIManager.put("nimbusBase", controlBackgroundUI);
UIManager.put("nimbusBlueGrey", controlBackgroundUI);
UIManager.put("nimbusFocus", focusUI);
All implemented in Java 7u55, though I doubt that matters as nobody seems to have maintained Swing/Nimbus for quite some time.
PS: I have, of course, read this question and others, but have not found an answer that works.
EDIT: here is an SSCCE that demonstrates the problem. It creates a JFrame with a default-only combobox on top, a list in the middle and a per-component-overriden combobox at the bottom. The problem can be seen when selecting a value in the list or from the box dropdowns.
package sscce;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.ColorUIResource;
public class ForegroundProblemDemo extends JFrame {
public ForegroundProblemDemo() {
super("Demo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComboBox<String> comboBoxWithDefaults = createComboBox();
JComboBox<String> comboBoxWithOverrides = createComboBox();
JList<String> list = createList();
addOverrides(comboBoxWithOverrides);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(new JScrollPane(list), BorderLayout.CENTER);
getContentPane().add(comboBoxWithDefaults, BorderLayout.NORTH);
getContentPane().add(comboBoxWithOverrides, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
JComboBox<String> createComboBox() {
JComboBox<String> comboBox = new JComboBox<>(new String[] {"A","B","C","D"});
return comboBox;
}
JList<String> createList() {
JList<String> list = new JList<>(new String[] {"A","B","C","D"});
return list;
}
void addOverrides(JComponent component) {
UIDefaults theme = new UIDefaults();
theme.put("nimbusLightBackground", new Color(0xFFFAFA));
theme.put("ComboBox:\"ComboBox.listRenderer\"[Selected].textForeground", new Color(0xFFFAFA));
component.putClientProperty("Nimbus.Overrides.InheritDefaults", true);
component.putClientProperty("Nimbus.Overrides", theme);
SwingUtilities.updateComponentTreeUI(component);
}
public static void main(String... args) throws Throwable {
ColorUIResource backgroundUI = new ColorUIResource(0x494949);
ColorUIResource textUI = new ColorUIResource(0xFFFAFA);
ColorUIResource controlBackgroundUI = new ColorUIResource(0x5F5F4D);
ColorUIResource infoBackgroundUI = new ColorUIResource(0x2f5cb4);
ColorUIResource infoUI = new ColorUIResource(0x2f5cb4);
ColorUIResource lightBackgroundUI = new ColorUIResource(0x5D5D5B);
ColorUIResource focusUI = new ColorUIResource(0x39698a);
UIManager.put("control", backgroundUI);
UIManager.put("text", textUI);
UIManager.put("nimbusLightBackground", lightBackgroundUI);
UIManager.put("info", infoUI);
UIManager.put("nimbusInfoBlue", infoBackgroundUI);
UIManager.put("nimbusBase", controlBackgroundUI);
UIManager.put("nimbusBlueGrey", controlBackgroundUI);
UIManager.put("nimbusFocus", focusUI);
for (LookAndFeelInfo lafInfo : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(lafInfo.getName())) {
UIManager.setLookAndFeel(lafInfo.getClassName());
break;
}
}
new ForegroundProblemDemo();
}
}
EDIT 2: Sorry, should have mentioned this before: For the list the problem is easily resolved with the setSelectionForeground() method. For ComboBoxes, I have yet to find a way short of custom renderers. So my main focus here is on the ComboBoxes.
import java.awt.*;
import java.util.Vector;
import javax.swing.*;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.nimbus.AbstractRegionPainter;
public class MyComboBox {
private Vector<String> listSomeString = new Vector<String>();
private JComboBox someComboBox = new JComboBox(listSomeString);
private JComboBox editableComboBox = new JComboBox(listSomeString);
private JComboBox non_EditableComboBox = new JComboBox(listSomeString);
private JFrame frame;
public MyComboBox() {
listSomeString.add("-");
listSomeString.add("Snowboarding");
listSomeString.add("Rowing");
listSomeString.add("Knitting");
listSomeString.add("Speed reading");
someComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
someComboBox.setFont(new Font("Serif", Font.BOLD, 16));
someComboBox.setEditable(true);
someComboBox.getEditor().getEditorComponent().setBackground(Color.YELLOW);
((JTextField) someComboBox.getEditor().getEditorComponent()).setBackground(Color.YELLOW);
editableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
editableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
editableComboBox.setEditable(true);
JTextField text = ((JTextField) editableComboBox.getEditor().getEditorComponent());
text.setBackground(Color.YELLOW);
/*JComboBox coloredArrowsCombo = editableComboBox;
Component[] comp = coloredArrowsCombo.getComponents();
for (int i = 0; i < comp.length; i++) {
if (comp[i] instanceof MetalComboBoxButton) {
MetalComboBoxButton coloredArrowsButton = (MetalComboBoxButton) comp[i];
coloredArrowsButton.setBackground(null);
break;
}
}*/
non_EditableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
non_EditableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
frame = new JFrame();
frame.setLayout(new GridLayout(0, 1, 10, 10));
frame.add(someComboBox);
frame.add(editableComboBox);
frame.add(non_EditableComboBox);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
try {
for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(laf.getName())) {
UIManager.setLookAndFeel(laf.getClassName());
UIManager.getLookAndFeelDefaults().put("ComboBox[Enabled].backgroundPainter",
new javax.swing.plaf.nimbus.AbstractRegionPainter() {
@Override
protected AbstractRegionPainter.PaintContext getPaintContext() {
return new AbstractRegionPainter.PaintContext(null, null, false);
}
@Override
protected void doPaint(Graphics2D g, JComponent c,
int width, int height, Object[] extendedCacheKeys) {
g.setColor(Color.MAGENTA);
g.fill(new Rectangle(0, 0, width, height));
}
});
UIManager.getLookAndFeelDefaults().put("ComboBox[Focused+Pressed].backgroundPainter",
new javax.swing.plaf.nimbus.AbstractRegionPainter() {
@Override
protected AbstractRegionPainter.PaintContext getPaintContext() {
return new AbstractRegionPainter.PaintContext(null, null, false);
}
@Override
protected void doPaint(Graphics2D g, JComponent c,
int width, int height, Object[] extendedCacheKeys) {
g.setColor(Color.CYAN);
g.fill(new Rectangle(0, 0, width, height));
}
});
UIManager.getLookAndFeelDefaults().put("ComboBox[Focused].backgroundPainter",
new javax.swing.plaf.nimbus.AbstractRegionPainter() {
@Override
protected AbstractRegionPainter.PaintContext getPaintContext() {
return new AbstractRegionPainter.PaintContext(null, null, false);
}
@Override
protected void doPaint(Graphics2D g, JComponent c,
int width, int height, Object[] extendedCacheKeys) {
g.setColor(Color.RED);
g.fill(new Rectangle(0, 0, width, height));
}
});
}
}
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
MyComboBox aCTF = new MyComboBox();
}
});
}
}
nothing happends without using an XxxRenderer, then is possible to create nice theme for editable and non_editable JComboBox (with the same Color theme)
I'd be look at Seaglas L&F (note to required compiled in JDK 1.6._Xxx)
import java.awt.*;
import java.util.Vector;
import javax.swing.*;
import javax.swing.UIManager;
public class MyComboBox {
private Vector<String> listSomeString = new Vector<String>();
private JComboBox someComboBox = new JComboBox(listSomeString);
private JComboBox editableComboBox = new JComboBox(listSomeString);
private JComboBox non_EditableComboBox = new JComboBox(listSomeString);
private JFrame frame;
public MyComboBox() {
listSomeString.add("-");
listSomeString.add("Snowboarding");
listSomeString.add("Rowing");
listSomeString.add("Knitting");
listSomeString.add("Speed reading");
someComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
someComboBox.setFont(new Font("Serif", Font.BOLD, 16));
someComboBox.setEditable(true);
someComboBox.getEditor().getEditorComponent().setBackground(Color.YELLOW);
((JTextField) someComboBox.getEditor().getEditorComponent()).setBackground(Color.YELLOW);
editableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
editableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
editableComboBox.setEditable(true);
JTextField text = ((JTextField) editableComboBox.getEditor().getEditorComponent());
text.setBackground(Color.YELLOW);
non_EditableComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
non_EditableComboBox.setFont(new Font("Serif", Font.BOLD, 16));
frame = new JFrame();
frame.setLayout(new GridLayout(0, 1, 10, 10));
frame.add(someComboBox);
frame.add(editableComboBox);
frame.add(non_EditableComboBox);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.seaglasslookandfeel.SeaGlassLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
MyComboBox aCTF = new MyComboBox();
}
});
}
}