Skip to main content

paint error inside a JTabbedPane

6 replies [Last post]
tbee
Offline
Joined: 2003-07-23
Points: 0

I have a lazy loading tabbed pane. To visualize this I wrap each component tree in a tab in a JXLayer with LockableUI. The LockableUI is slightly extended so that when it is locked a refresh icon is painted.

When the context of the tabbed pane is changed, all JXLayers (RefreshGlasspane) are set to locked, thus showing the icon. And then as each tab is clicked-to-front, the refresh icon is hidden.

Of this situation I have made a mock up only showing the JXLayer and icon painting part.

The setup is simple:
- create a tabbed pane that uses "fireStateChanged()" to set the active tab's lockableUI.setLocked() to false.
- create a button that sets all locked state to true
- for ease of testing I replaced the image with a semitransparent square

What is the problem? Well, of the tab visible when the lockable state is set to true, then icon never is hidden, even though the lockable state is set to false and the JXLayer is repainted.

Am I making a mistake?

<br />
import java.awt.BorderLayout;<br />
import java.awt.Component;<br />
import java.awt.Graphics2D;<br />
import java.awt.event.ActionEvent;<br />
import java.awt.event.ActionListener;</p>
<p>import javax.swing.ImageIcon;<br />
import javax.swing.JButton;<br />
import javax.swing.JComponent;<br />
import javax.swing.JFrame;<br />
import javax.swing.JLabel;<br />
import javax.swing.JTabbedPane;<br />
import javax.swing.SwingUtilities;</p>
<p>import org.jdesktop.jxlayer.JXLayer;<br />
import org.jdesktop.jxlayer.plaf.ext.LockableUI;</p>
<p>public class Test extends JFrame<br />
{<br />
    public static void main(String[] args)<br />
    {<br />
        SwingUtilities.invokeLater(new Runnable()<br />
        {<br />
            @Override<br />
            public void run()<br />
            {<br />
                gui();<br />
            }<br />
        });<br />
    }</p>
<p>    public static void gui()<br />
    {<br />
        try<br />
        {<br />
            // create a jtabbed pane that will set the selected tab's "refreshing" JXLayer to false<br />
            JTabbedPane lJTabbedPane = new JTabbedPane()<br />
            {<br />
                protected void fireStateChanged()<br />
                {<br />
                    // do normal behavior<br />
                    super.fireStateChanged();</p>
<p>                    // load selected tab<br />
                    int lSelectedIndex = getSelectedIndex();<br />
                    Component lSelectedComponent = getComponentAt(lSelectedIndex);<br />
                    System.out.println(lSelectedComponent.getName());<br />
                    RefreshGlasspane.setRefreshing(lSelectedComponent, false);<br />
                }<br />
            };</p>
<p>            // create two tabs each contents wrapped in a "refreshing" JXLayer<br />
            final Component lRefreshGlasspane1 = RefreshGlasspane.wrap(new JLabel("placeholder"));<br />
            lRefreshGlasspane1.setName("g1");<br />
            lJTabbedPane.addTab("t1", lRefreshGlasspane1);<br />
            final Component lRefreshGlasspane2 = RefreshGlasspane.wrap(new JLabel("placeholder"));<br />
            lRefreshGlasspane2.setName("g2");<br />
            lJTabbedPane.addTab("t2", lRefreshGlasspane2);</p>
<p>            // create a button that sets both "refreshing" JXLayers to true<br />
            JButton lJButton = new JButton("Mark tabs as to-be-refreshed");<br />
            lJButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e)<br />
            {<br />
                RefreshGlasspane.setRefreshing(lRefreshGlasspane1, true);<br />
                RefreshGlasspane.setRefreshing(lRefreshGlasspane2, true);<br />
            }});</p>
<p>            // everything in a JFrame<br />
            JFrame lJFrame = new JFrame();<br />
            lJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
            lJFrame.setLayout(new BorderLayout());<br />
            lJFrame.add(lJButton, BorderLayout.NORTH);<br />
            lJFrame.add(lJTabbedPane, BorderLayout.CENTER);<br />
            lJFrame.setSize(400,400);<br />
            lJFrame.setVisible(true);</p>
<p>            // emulate a click to start with<br />
            lJButton.doClick();<br />
        }<br />
        catch (Throwable e) { e.printStackTrace(); }<br />
        System.out.println( "done");<br />
    }</p>
<p>    static public class RefreshGlasspane<br />
    {<br />
        static public Component wrap(JComponent jcomponent)<br />
        {<br />
            // create JXLayer<br />
            final JXLayer lJXLayer = new JXLayer(jcomponent);</p>
<p>            // create extended LockableUI<br />
            LockableUI lAbstractLayerUI = new LockableUI()<br />
            {<br />
                @Override<br />
                protected void paintLayer(Graphics2D g2, JXLayer jxlayer)<br />
                {<br />
                    // this paints layer as is<br />
                    super.paintLayer(g2, jxlayer);</p>
<p>                    // custom painting<br />
                    System.out.println(lJXLayer.getName() + " repainting, locked=" + isLocked());<br />
                    if (isLocked())<br />
                    {<br />
                        // paint the refreshing mode<br />
                        //g2.drawImage(iRefreshingIcon.getImage(), (jxlayer.getWidth() - 48) / 2, (jxlayer.getHeight() - 48) / 2, null);<br />
                        // alternate code;<br />
                        g2.setColor(new Color(128, 128, 128, 100));<br />
                        g2.fillRect((jxlayer.getWidth() - 48) / 2, (jxlayer.getHeight() - 48) / 2, 48, 48);<br />
                    }<br />
                }<br />
            };</p>
<p>            // set our UI<br />
            lJXLayer.setUI(lAbstractLayerUI);</p>
<p>            // done<br />
            return lJXLayer;<br />
        }<br />
        //static private ImageIcon iRefreshingIcon = new ImageIcon(org.tbee.swing.glasspane.RefreshGlasspane.class.getResource("icon_refreshing48x48.png"));</p>
<p>        /**<br />
         *<br />
         * @param panel<br />
         * @param value<br />
         */<br />
        static public void setRefreshing(Component panel, boolean value)<br />
        {<br />
            ((LockableUI)((JXLayer) panel).getUI()).setLocked(value);<br />
        }</p>
<p>    }</p>
<p>}<br />

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
tbee
Offline
Joined: 2003-07-23
Points: 0

Yes. That did the trick.

Do you mind explaining why paintLayer did not work?

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Tbee

Sure, sorry I didn't explain it in the first message

Actually I didn't extensively debug it, but here is my ideas from the top of my head:

there is special method in the AbstractBufferedLayerUI class - isIncrementalUpdate(),
when it returns false, it means that the layerUI's buffer is not updated when the layer is repainted

LockableUI.isIncrementalUpdate() returns false when the ui is in the locked state

LockableUI.paint() is called every time the layer is repainted, that's why it must work all the time. I suspect that the layer can't correctly repaint itself when it is invisible and it causes a problem, I should have a deeper look at this question when I work on the next version of the layer component.

Thanks
alexp

tbee
Offline
Joined: 2003-07-23
Points: 0

Thanks!

This will make the Synthetica developers happy, because I only ran into this problem in 50% of their skins. All other LaFs seem to do some additional refreshing which make the icon go away. So either it was JXLayer or Synthentica. :-)

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Tbee

Nice to hear that!

Have a nice day
alexp

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Tbee

Override LockableUI.paint instead of paintLayer()

By the way, RefreshGlasspane class looks really awkward in my opinion,
besides the really strange name, I'd implement all those methods inside the LockableUI subclass

Thanks
alexp

tbee
Offline
Joined: 2003-07-23
Points: 0

Yeah, well, that may to do with the fact that this is an existing implementation that was retrofitted to use JXLayer. Or maybe I do not understand what you mean...

The initial idea was to have a glasspane over a component on which a refreshing icon can be displayed; hence the name "(BusyWith)RefreshingGlasspane".

Secondly I used to wrap the component in another panel. Then I modified that to extend JXLayer, and later I modded it again for JXLayer 3.0, which is final, so that kinda ruined that approach and I had to bend the coding a bit in order not to have to rewrite the app.

In fact; this example is already cleaned up, since i actually still push a JPanel in between. Legacy legacy...

But I'll give that paint a try... Although I remember that the documentation says to override paintLayer.