tags:

views:

2164

answers:

4

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); }

Example

alt text

A: 

Hey there Software Monkey.

mmhh what about installing UI's subclasses replacement that actually respect the setOpaque behavior.

I think it is something like setUI or something like that.

You could grab the source code of the nimbus and see what's broken there ( if it is there ) , subclass it and install the "fixed" one.

Yours sound quite intersting, do you have any screenshot we can see?

OscarRyz
If the only choice was this, I think I would stick with the system LAF. At least then I am less likely to have surprises when running in Linux, OSX, and others.
Software Monkey
+6  A: 

I ran into this same issue last week using JTextPane. The setOpaque() method works as expected when using any look and feel other than nimbus. Apparently, the nimbus look and feel changes the behaviour we have come to expect with setOpaque() for many Components. Depending on how you look at it, it can be considered a bug. Check the comments on this sun bugid:

nimbus opaque bug

The workaround that worked for me was:

myPane.setOpaque(false); // added by OP
myPane.setBorder(BorderFactory.createEmptyBorder());
myPane.setBackground(new Color(0,0,0,0));


Note from OP: I also had to ensure setOpaque(false) for JTextField so that the parent background was painted - just wanted to mention this for others who follow in case they had experimented with setOpaque(true), as I had.

Gary
This looks promising; will try it tonight in my app. Thanks.
Software Monkey
@juggler555: Thanks, that works, provided the component is *not* opaque (when I left it opaque, text updates "scribbled" over the top of the previous).
Software Monkey
Yup, its not perfect but it worked for me.
Gary
A: 

I think the question is how to interpret "opaque" and "background". For a JTextfield there is the question: "what visible parts are the background?". I'd define "background" as the parts of the bounding rectangle, that are not drawn by the component. For a "round" button, e.g., this will be the corners outside the circle. Therefor I'd say a JTextfield has no visible background! It has a rectangular shape and what you are the taking as background is not the field's background but the field's canvas.


Rebuttal from OP

This is an interesting enough idea to be worth responding to in the answer for future viewers (as opposed to in comments).

I have to disagree. I would argue that the part of the component outside the border is not part of the component - it's outside the component. A field with rounded corners is, of necessity, non-opaque, in that it cannot be responsible for painting it's entire rectangular region - this is a side-effect of all components being rectangular in dimensions.

I think this consideration makes the argument for the existing (and misunderstood) meaning of isOpaque(). It also makes my argument that setOpaque() should not exist and that setBackground(null) should cause the component to not paint a background.

I would put forth that the background of a text field is indeed the color of the region inside it's borders, and I don't think you will find very many people to dispute that as an intuitive conclusion - therefore having background apply to that region obeys the rule of least surprise for the API user.

ordnungswidrig
The bug refered to above seems to tell the same story.
ordnungswidrig
@ordnungswidrig: Your answer is hard to decipher, but I am hesitant to edit it and risk putting words in your mouth - do you want to make a second pass at it, for others who come along?
Software Monkey
@Ordnungswidrig: Interesting thought - I added a rebuttal to your answer, since I thought this was an important idea.
Software Monkey
A: 

From the javadoc

public void setBackground(Color bg)

Sets the background color of this component. The background color is used only if the component is opaque, and only by subclasses of JComponent or ComponentUI implementations. Direct subclasses of JComponent must override paintComponent to honor this property.

It is up to the look and feel to honor this property, some may choose to ignore it.

Martin OConnor
And, as I said, it's a shame Nimbus has chosen to ignore it.
Software Monkey
But its worse. The doc says that it's only honored when setOpaque is true.
Martin OConnor