Java Nimbus LAF with transparent text fields
- by Software Monkey
I have an application that uses disabled JTextFields in several places which are intended to be transparent - allowing the background to show through instead of the text field's normal background.
When running the new Nimbus LAF these fields are opaque (despite setting setOpaque(false)), and my UI is broken. It's as if the LAF is ignoring the opaque property. Setting a background color explicitly is both difficult in several places, and less than optimal due to background images actually doesn't work - it still paints it's LAF default background over the top, leaving a border-like appearance (the splash screen below has the background explicitly set to match the image).
Any ideas on how I can get Nimbus to not paint the background for a JTextField?
Note: I need a JTextField, rather than a JLabel, because I need the thread-safe setText(), and wrapping capability.
Note: My fallback position is to continue using the system LAF, but Nimbus does look substantially better.
See example images below.
Conclusions
The surprise at this behavior is due to a misinterpretation of what setOpaque() is meant to do - from the Nimbus bug report:
This is a problem the the orginal design of Swing and how it has been confusing for years. The issue is setOpaque(false) has had a side effect in exiting LAFs which is that of hiding the background which is not really what it is ment for. It is ment to say that the component my have transparent parts and swing should paint the parent component behind it.
It's unfortunate that the Nimbus components also appear not to honor setBackground(null) which would otherwise be the recommended way to stop the background painting. Setting a fully transparent background seems unintuitive to me.
In my opinion, setOpaque()/isOpaque() is a faulty public API choice which should have been only:
public boolean isFullyOpaque();
I say this, because isOpaque()==true is a contract with Swing that the component subclass will take responsibility for painting it's entire background - which means the parent can skip painting that region if it wants (which is an important performance enhancement). Something external cannot directly change this contract (legitimately), whose fulfillment may be coded into the component.
So the opacity of the component should not have been settable using setOpaque(). Instead something like setBackground(null) should cause many components to "no long have a background" and therefore become not fully opaque. By way of example, in an ideal world most components should have an isOpaque() that looks like this:
public boolean isOpaque() { return (background!=null); }