Skip to main content

LockableUI painting problem when locked (continued thread)

8 replies [Last post]
divxdede
Offline
Joined: 2007-09-01

This thread continue this one : http://forums.java.net/jive/thread.jspa?threadID=44315&tstart=0

So, i wrote a new Test Case that consist of:

A LayerUI (LockableUI subclass) that render on the glassPane a JXBusyLabel.
This label can be moving up and down with controls provided by the frame.

The frame own another control for "lock" or "unlock" the LayerUI.

How produce the bug ?
move up and down without locking the LayerUI and all work fine.
Lock the UI and move up and down, you should see an old "rubber stamp" of the label at an older position.

<br />
package org.divxdede.swing.busy.issue2;</p>
<p>import java.awt.Dimension;<br />
import java.awt.GridLayout;<br />
import java.awt.event.ActionEvent;<br />
import java.awt.event.ActionListener;<br />
import javax.swing.JButton;<br />
import javax.swing.JComponent;<br />
import javax.swing.JFrame;<br />
import javax.swing.JPanel;<br />
import javax.swing.JTree;<br />
import javax.swing.SwingUtilities;<br />
import org.jdesktop.jxlayer.JXLayer;</p>
<p>/**<br />
 * Reveal a painting issue using LockableUI<br />
 * @author André Sébastien<br />
 */<br />
public class Issue2 implements ActionListener {</p>
<p>    JButton buttonUp         = null;<br />
    JButton buttonDown       = null;<br />
    JButton buttonLockUnlock = null;</p>
<p>    JXLayer layer            = null;</p>
<p>    Issue2() {<br />
        /** Create and configure the frame<br />
         */<br />
        JFrame frame = new JFrame("Issue LockableUI #2");<br />
        frame.getContentPane().setLayout( new GridLayout(1,2) );<br />
        frame.getContentPane().add( createTestedLayer() );<br />
        frame.getContentPane().add( createControlPanel() );</p>
<p>        frame.pack();<br />
        frame.setSize( new Dimension(300,300) );<br />
        frame.setLocationRelativeTo(null);<br />
        frame.setDefaultCloseOperation( frame.EXIT_ON_CLOSE );<br />
        frame.setVisible(true);<br />
    }</p>
<p>    /** Create the controler panel<br />
     */<br />
    private JComponent createControlPanel() {<br />
        JPanel panel = new JPanel();<br />
        panel.setLayout( new GridLayout(3,1) );</p>
<p>        buttonUp         = new JButton("up");<br />
        buttonDown       = new JButton("down");<br />
        buttonLockUnlock = new JButton("lock");</p>
<p>        panel.add( buttonUp );<br />
        panel.add( buttonDown );<br />
        panel.add( buttonLockUnlock );</p>
<p>        buttonUp.addActionListener(this);<br />
        buttonDown.addActionListener(this);<br />
        buttonLockUnlock.addActionListener(this);</p>
<p>        return panel;<br />
    }</p>
<p>    /** Create the JXLayer to test<br />
     */<br />
    private JXLayer createTestedLayer() {</p>
<p>        this.layer = new JXLayer( new JTree() );<br />
        this.layer.setUI( new IssueLayerUI() );</p>
<p>        return layer;<br />
    }</p>
<p>    public void actionPerformed(ActionEvent e) {<br />
        if( e.getSource() == buttonDown ) {<br />
            getLayerUI().doDown();<br />
        }<br />
        else if( e.getSource() == buttonUp ) {<br />
            getLayerUI().doUp();<br />
        }<br />
        else if( e.getSource() == buttonLockUnlock ) {<br />
            getLayerUI().setLocked( !getLayerUI().isLocked() );<br />
            if( getLayerUI().isLocked() )<br />
                buttonLockUnlock.setText("unlock");<br />
            else<br />
                buttonLockUnlock.setText("lock");<br />
        }<br />
    }    </p>
<p>    IssueLayerUI getLayerUI() {<br />
        return (IssueLayerUI)this.layer.getUI();<br />
    }</p>
<p>    /** main without arg<br />
     */<br />
    public static void main(String[] args) {</p>
<p>        Runnable doRun = new Runnable() {<br />
            public void run() {<br />
                new Issue2();<br />
            }<br />
        };<br />
        SwingUtilities.invokeLater(doRun);<br />
    }<br />
}<br />

<br />
package org.divxdede.swing.busy.issue2;</p>
<p>import java.awt.Color;<br />
import java.awt.Component;<br />
import java.awt.Container;<br />
import java.awt.Dimension;<br />
import java.awt.Graphics2D;<br />
import java.awt.LayoutManager;<br />
import java.awt.geom.Rectangle2D;<br />
import javax.swing.JComponent;<br />
import javax.swing.JPanel;<br />
import org.jdesktop.jxlayer.JXLayer;<br />
import org.jdesktop.jxlayer.plaf.ext.LockableUI;<br />
import org.jdesktop.swingx.JXBusyLabel;</p>
<p>/**<br />
 *<br />
 * @author André Sébastien<br />
 */<br />
public class IssueLayerUI extends LockableUI{</p>
<p>    static final Color COLOR_LOCK      = new Color(200,150,120,200);<br />
    static final Color COLOR_TEXT_LOCK = new Color(50,50,50);</p>
<p>    /** Members<br />
     */<br />
    private JXBusyLabel  label  = null;<br />
    private CustomLayout layout = null;</p>
<p>    @Override<br />
    public void installUI(JComponent c) {<br />
        super.installUI(c);<br />
        JXLayer layer = (JXLayer)c;<br />
        layer.setGlassPane( createBusyPanel() );<br />
    }</p>
<p>    @Override<br />
    protected void paintLayer(Graphics2D g2, JXLayer l) {<br />
        super.paintLayer(g2, l);<br />
        if( isLocked() ) {<br />
            Color oldColor = g2.getColor();</p>
<p>            g2.setColor( COLOR_LOCK );<br />
            int w = l.getWidth() - 30 ;<br />
            int h = 60;</p>
<p>            int x = l.getX() + 15;<br />
            int y = l.getY() + 15;<br />
            g2.fillRoundRect( x , y , w, h , 15 , 15 );</p>
<p>            g2.setColor( COLOR_TEXT_LOCK );<br />
            String      toDraw     = "LOCKED";<br />
            Rectangle2D textBounds = g2.getFont().getStringBounds(toDraw , g2.getFontRenderContext() );<br />
            int xText = x + ( ( w - (int)textBounds.getWidth() ) / 2 );<br />
            int yText = y + (int)textBounds.getHeight();<br />
            g2.drawString( toDraw , xText , yText );</p>
<p>            g2.setColor(oldColor);<br />
        }<br />
    }</p>
<p>    /** Create the busy panel<br />
     */<br />
    JComponent createBusyPanel() {<br />
        label = new JXBusyLabel();<br />
        label.setHorizontalAlignment( label.CENTER );<br />
        label.setVerticalAlignment( label.CENTER );<br />
        label.setHorizontalTextPosition( label.CENTER );<br />
        label.setVerticalTextPosition( label.BOTTOM );<br />
        label.setBusy(true);</p>
<p>        layout = new CustomLayout();</p>
<p>        JPanel panel = new JPanel();<br />
        panel.setOpaque(false);<br />
        panel.setLayout( layout );<br />
        panel.add( label );</p>
<p>        return panel;<br />
    }</p>
<p>    /** Move UP the busy label<br />
     *  Be Careful: Out Of Range is NOT tested<br />
     */<br />
    public void doUp() {<br />
        this.layout.incrOffset( -10 );<br />
        this.label.revalidate();<br />
    }</p>
<p>    /** Move DOWN the busy label<br />
     *  Be Careful: Out Of Range is NOT tested<br />
     */<br />
    public void doDown() {<br />
        this.layout.incrOffset( +10 );<br />
        this.label.revalidate();<br />
    }</p>
<p>    /** Dummy container allowig to center move up/down a component.<br />
     *  Container using this layout can have only ONE component<br />
     *  This layout don't<br />
     * (it's for this test purpose only)<br />
     */<br />
    public static class CustomLayout implements LayoutManager {</p>
<p>        int offset = 0;</p>
<p>        public void addLayoutComponent(String name, Component comp) {<br />
            // nothing to do<br />
        }</p>
<p>        public void removeLayoutComponent(Component comp) {<br />
            // nothing to do<br />
        }</p>
<p>        public Dimension preferredLayoutSize(Container parent) {<br />
            for( Component c : parent.getComponents() )  {<br />
                return c.getPreferredSize();<br />
            }<br />
            return null;<br />
        }</p>
<p>        public Dimension minimumLayoutSize(Container parent) {<br />
            for( Component c : parent.getComponents() )  {<br />
                return c.getMinimumSize();<br />
            }<br />
            return null;<br />
        }</p>
<p>        public void layoutContainer(Container parent) {<br />
            for( Component c : parent.getComponents() )  {<br />
                Dimension pref = c.getPreferredSize();<br />
                int       x = (parent.getWidth() / 2 ) - (pref.width / 2);<br />
                int       y = (parent.getHeight() / 2 ) - (pref.height / 2);</p>
<p>                y = y + offset;</p>
<p>                c.setBounds(x,y , pref.width , pref.height );<br />
            }<br />
        }</p>
<p>        /** Define a vertical offset<br />
         */<br />
        public synchronized void setOffset( int offset ) {<br />
            this.offset = offset;<br />
        }</p>
<p>        /** Returns the vertical offset<br />
         */<br />
        public synchronized int getOffset() {<br />
            return this.offset;<br />
        }</p>
<p>        /** Modify an offset by adding a sub-offset<br />
         */<br />
        public synchronized void incrOffset( int incr ) {<br />
            setOffset( getOffset() + incr );<br />
        }<br />
    }<br />
}<br />

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
alexfromsun
Offline
Joined: 2005-09-05

Hello Andre

When I run your test I didn't see any sign of the busy label on the layer's glassPane,
the reason may be the old version of SwingX I have on my computer.
Anyway, after you move a component on the layer's glassPane
you should mark the layer as dirty:

[code]
public void doUp() {
this.layout.incrOffset( -10 );
label.revalidate();
setDirty(true);
}
[/code]

The same is for doDown()

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01

I will see if this tip help me.

Can you try with a newer version of swingX ? i use the 0.9.3 on my development platform.

If the glasspane is a complex container that have it's own ui logic and control.
When some of their childs is lay out (invalidate/revalidate), we must monitor it and call the setDirty() manually ?

Message was edited by: divxdede

divxdede
Offline
Joined: 2007-09-01

The setDirty() works fine.

But have you a tip for manage automatically a setDirty() on the layerUI when a dirty region is set on a components of the glasspane (vy invalidate & co) ?

alexfromsun
Offline
Joined: 2005-09-05

Hello Andre

When you move a component on a layer's glassPane
you should manually call setDirty(true) in a special case:

when you use an AbstractBufferedUI or its subclass (like LockedLayerUI)
and its isIncrementalUpdate() method returns false,
just like LockedLayerUI does in the locked state

When it is locked, the view component is invisible
and the buffered image is drawn instead

setDirty(true) means "repaint the buffer"
and I prefer to call it manually,
because in this case you have more control on the painting process

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01

I understand,

So, with this things in mind, i was able to achieve my design and all works fine.

Thanks very much for your help.

PS: at this step, i'm afraid to fire tons of repaint events whithout knowing how verify this point...

alexfromsun
Offline
Joined: 2005-09-05

Hello Andre

You are welcome

> at this step, i'm afraid to fire tons of repaint events whithout knowing how verify this point...

look at the layer's source and explore how it works
:-)

Anyway calling setDirty(true) is inevitable in case you need to refresh the buffer
of an AbstractBufferedLayerUI when incrementatlUpdate flag is switched off
(it is like LockableUI behave in the locked state)

alexp

divxdede
Offline
Joined: 2007-09-01

Hello,

Just for let a feedback of my JXLayer's integration.
You can find here my (very little) project http://code.google.com/p/jbusycomponent/

And you can found a java web start demo here : http://www.jprintcover.com/jbusycomponent/jnlp/launch.php

Thanks for you help,
I have a last question, your API is under BSD licence, can i use it into a LGPL licence ? (i think yes, but i'm not completely sure)

Best regards,
André Sébastien.

alexfromsun
Offline
Joined: 2005-09-05

Hello André

Thank you for the links
the web start demo looks great!

You can certainly use JXLayer together with any code
as long as you respect the terms of the BSD license
you can read them in any JXLayer source file

Thanks
alexp