Non-opaque JButton background in non top level window becomes opaque?
Asked Answered
J

2

7

Before you read, here are some clarifications on what the question is about:

  1. The SSCCE is designed for Java 7. It would be possible to use sun.*.AWTUtilities to adapt it to Java 6, but it does not matter to me how it works on Java 6.
  2. The faulting line is [...]new JDialog(someWindow). Ghosting can be fixed in the SSCCE by simply changing that line to [...]new JDialog().

Why don't top level windows exhibit ghosting?


Expected behavior: final JDialog d = new JDialog() (see SSCCE) both TL and non-TL windows have translucent background

As you can see, the right window has a semitransparent background (as expected).

Actual behavior: final JDialog d = new JDialog(f) (see SSCCE) TL window shows translucent background, while non-TL background becomes opaque even after a single repaint

In this case, the right window has an opaque background. As a matter of fact, it takes 3-4 repaints due to any reason (easiest to reproduce is repaint on rollover) for the background to become completely opaque.


SSCCE:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.synth.ColorType;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthConstants;
import javax.swing.plaf.synth.SynthContext;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthPainter;
import javax.swing.plaf.synth.SynthStyle;
import javax.swing.plaf.synth.SynthStyleFactory;

public class SynthSSCCE
{
        public static void main(String[] args) throws Exception
        {
                final SynthLookAndFeel laf = new SynthLookAndFeel();
                UIManager.setLookAndFeel(laf);
                SynthLookAndFeel.setStyleFactory(new StyleFactory());

                SwingUtilities.invokeLater(new Runnable()
                {
                        @Override
                        public void run()
                        {
                                final JFrame f = new JFrame();
                                {
                                        f.add(new JButton("Works properly"));
                                        f.setUndecorated(true);
                                        f.setBackground(new Color(0, true));
                                        f.setSize(300, 300);
                                        f.setLocation(0, 0);
                                        f.setVisible(true);
                                }
                                {
                                        final JDialog d = new JDialog(f);
                                        final JButton btn = new JButton("WTF?");
                                        // uncomment and notice that this has no effect
                                        // btn.setContentAreaFilled(false);
                                        d.add(btn);
                                        d.setUndecorated(true);
                                        d.setBackground(new Color(0, true));
                                        d.setSize(300, 300);
                                        d.setLocation(320, 0);
                                        d.setVisible(true);
                                }
                        }
                });
        }

        static class StyleFactory extends SynthStyleFactory
        {
                private final SynthStyle style = new Style();

                @Override
                public SynthStyle getStyle(JComponent c, Region id)
                {
                        return style;
                }
        }

        static class Style extends SynthStyle
        {
                private final SynthPainter painter = new Painter();

                @Override
                protected Color getColorForState(SynthContext context, ColorType type)
                {
                        if (context.getRegion() == Region.BUTTON && type == ColorType.FOREGROUND)
                                return Color.GREEN;

                        return null;
                }

                @Override
                protected Font getFontForState(SynthContext context)
                {
                        return Font.decode("Monospaced-BOLD-30");
                }

                @Override
                public SynthPainter getPainter(SynthContext context)
                {
                        return painter;
                }

                @Override
                public boolean isOpaque(SynthContext context)
                {
                        return false;
                }
        }

        static class Painter extends SynthPainter
        {
                @Override
                public void paintPanelBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
                {
                        final Graphics g2 = g.create();
                        try
                        {
                                g2.setColor(new Color(255, 255, 255, 128));

                                g2.fillRect(x, y, w, h);
                        }
                        finally
                        {
                                g2.dispose();
                        }
                }

                @Override
                public void paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
                {
                        final Graphics g2 = g.create();
                        try
                        {
                                if ((context.getComponentState() & SynthConstants.MOUSE_OVER) == SynthConstants.MOUSE_OVER)
                                        g2.setColor(new Color(255, 0, 0, 255));
                                else
                                        g2.setColor(new Color(0xAA, 0xAA, 0xAA, 255));
                                g2.fillRoundRect(x, y, w, h, w / 2, h / 2);
                        }
                        finally
                        {
                                g2.dispose();
                        }
                }
        }
}

And these are my questions...

  1. What is going on? As in, why this exhibits behavior of a custom-painted non-opaque component that forgets to call super?
  2. Why doesn't it happen to TL windows?
  3. What is the easiest way to fix it, aside from not using non-TL windows?
Judicative answered 29/8, 2013 at 17:7 Comment(11)
Please paste your SSCCE code here, not in a link.Gouache
Replaced the link with full code.Judicative
no idea if is compiled in Java7Lashoh
@mKorbel: Unfortunately, code presented in that question/answers CLEARLY STAYS AWAY from any non-TL windows. I do not care that super("Test translucent window"); works fine, because I need non-TL windows, which unfortunately exhibit some strange behavior.Judicative
huuuh, but works for me in Java6 ...Lashoh
@mKorbel: I thought about adding a Java 6 SSCCE with reflection based AWTUtilities#setWindowOpaque(Window, boolean), but then again, Java 6 has been EoLed a long time ago. No use in investigating how this works on obsolete technologies...Judicative
interesting ...., you have to accepting that important is ordering of methods, code consistency, expected events ordering too, otherwise you are probably obsolete coder, sorry changes in Java7 breaking all standards, part of security rulles don't do that wihtout dirty hacks, if you want to distribute the hacks then to compile in Java7, rest of use Java6 or waiting to Java8, true is that any methods in Swing aren't changed together with changes in rest of the Java (Essential) ClassesLashoh
let us continue this discussion in chatJudicative
hmmm (tested again) now I confused see that as bug, now not sure if for Java6 or Java7, especially why is JDialog transparent in Win8/Java7, but be sure setContentAreaFilled works properly because you have to override paintText & paintIcon for JButton instance, create own SyntButtonUI, I'll post my testing as an answer hereLashoh
@mKorbel: 'you have to override paintText & paintIcon for JButton instance': if I wanted to have my own UI and trash swing's components, such as JOptionPane, JFileChooser etc. which will always use the default JButton/JPanel, then I would definitely do so. 'create own SyntButtonUI': that is not how Synth works. Yes, I have already thought about instrumenting Synth classes (because you cannot provide your own ComponentUIs), but I would like to do that as a last resort.Judicative
I'm talking about standard way, you can everything as you want to doLashoh
O
1

it takes 3-4 repaints due to any reason (easiest to reproduce is repaint on rollover) for the background to become completely opaque.

Check out Backgrounds With Transparency which should give you some insight into the problem.

I've never played with Synth so I don't know if the same solution will work or not.

Othilia answered 29/8, 2013 at 19:7 Comment(1)
Synth does not follow the general pre-Nimbus L&F consensus that an opaque component is such a component that paints its background and vice versa. Synth allows non-opaque components to paint their background as it should be (for certain components, isContentAreaFilled in UIDefaults controls whether background is painted). The main question is why everything works on TL windows but not on non-TL windows?Judicative
S
0

Why don't top level windows exhibit ghosting?

According to Oracle (Java Tutorials):

Each top-level container has a content pane that, generally speaking, contains (directly or indirectly) the visible components in that top-level container's GUI.

http://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html

enter image description here

The glass pane is often used to intercept input events occuring over the top-level container, and can also be used to paint over multiple components. It doesnt allow transparency.

Hence how you used

final Graphics g2 = g.create();

If you have the javax.swing.JComponent.paintComponent overrided in a method opposed to creating the graphics object yourself it should mitigate the transparency by super.g();

Fix this by creating a separate method listed above for graphics

Starlet answered 7/9, 2013 at 14:41 Comment(2)
seriously doubt that the glasspane has anything to do with the issue: f.i. how would that explain the different behaviour of new JDialog() vs. new JDialog(frame)?Alluvial
All components are added to the content pane. Glass pane is completely unused (can be seen in the SSCCE).Judicative

© 2022 - 2024 — McMap. All rights reserved.