Skip to main content

layout and painting problem

13 replies [Last post]
divxdede
Offline
Joined: 2007-09-01
Points: 0

Hi,

I'am currently writing a simple API allowing to manage "busy" components.
What it means ? it's standard components that own a BusyModel allowing to set or not theses components in a "busy" state.

When it's the case an animation is painted over the component (like a BusyPainter).

The main interrest of this api is the BusyModel that inherit of BoundedRangeModel. By this way this model can be in a "determinate" mode or not. On this case the animation is provided with a progress bar and a % counter.

In addition, the BusyModel increase functionnality with a "cancellable" property that allow the busy animation to provide a "cancel" hyperlink button (or not).

The last hint of this api is the special implementation of a BusyModel tracking the process of "Futures" used by the concurrent package.

Well, all of theses functionnality can be done easily by your impressive JXLayer API. And all works fine except some repaints (and maybe doLayout).

On the glassPane i put a JXPanel using a JXBusyLabel. when the JXBusyLabel is moved inside the Panel (after a doLayout), i keep an old painting version of the label at the old place.

I don't kwnow where is the problem
- In my use of JXLayer (or of the JXBusyLabel)
- a bug on JXLayer somewhere
- a bug on JXBusyLabel somewhere

A this point i'm not be able to determine the problem location...
If you can regard the code, you can download the netbeans project here : http://objectserver.googlecode.com/files/BusyHandler.zip

In the dist directory you have a sample that demonstrate what we talk.

Best regards,
ANDRE Sébastien.

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
Points: 0

Hello ANDRE

I am sorry, but I don't have time to download your zipped project

Please make your test case as small as possible
and post it here wrapped with [ code ] and [ /code ]
with no space in the brackets

also provide a detailed description of the problem

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01
Points: 0

Ok, let's go.

I'm writting a "sample" that simplify the test case.
And it let me to understand "where" the problem occurs.

And it's occurs when the LockableUI is in locked state.

An example here :

[code]
package org.divxdede.swing.busy.issue;

import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.JTree;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.ext.LockableUI;

/**
*
* @author André Sébastien
*/
public class IssueComponent extends JXLayer {

public IssueComponent( C view ) {
super(view);

IssuePanel panel = new IssuePanel();
panel.lockableUI = new LockableUI();

setGlassPane( panel );
setUI( panel.lockableUI );
}

public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add( new IssueComponent( new JTree() ) );
frame.pack();
frame.setSize( new Dimension(300,150) );
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation( frame.EXIT_ON_CLOSE );
frame.setVisible(true);
}
}
[/code]

and

[code]
package org.divxdede.swing.busy.issue;

import javax.swing.SwingUtilities;
import org.jdesktop.jxlayer.plaf.ext.LockableUI;
import org.jdesktop.swingx.JXPanel;

public class IssuePanel extends JXPanel {

javax.swing.JProgressBar jProgressBar;
org.jdesktop.swingx.JXBusyLabel jXBusyLabel;
private org.jdesktop.swingx.JXHyperlink jXHyperlinkCancel;
private org.jdesktop.swingx.JXPanel jXPanel1;

boolean visible = false;
boolean detailsVisible = false;
LockableUI lockableUI = null;

/** Creates new form BusyPanelImpl */
public IssuePanel() {
initComponents();
stateChanged();

Runnable doRun = new Runnable() {
public void run() {
sleep(1000);
visible = true;
stateChanged();

sleep(3000);
detailsVisible = true;
stateChanged();

sleep(3000);
detailsVisible = false;
stateChanged();

sleep(3000);
visible = false;
stateChanged();
}

private void sleep(long ms) {
try {
Thread.sleep(ms);
}
catch(InterruptedException ie) {
ie.printStackTrace();
}
}
};
Thread t = new Thread(doRun);
t.start();
}

/** Refresh the panel state from the current model state
*/
public void stateChanged() {
if( ! SwingUtilities.isEventDispatchThread() ) {
Runnable doRun = new Runnable() {
public void run() {
stateChanged();
}
};
SwingUtilities.invokeLater(doRun);
return;
}

/** ***********************************************
* IF YOU COMMENT THIS LINE, THE PROBLEM DISAPPEAR
* ***********************************************
*/
lockableUI.setLocked( visible );

this.jXBusyLabel.setVisible( visible );
this.jXBusyLabel.setBusy( visible );

this.jProgressBar.setVisible( detailsVisible );
this.jXHyperlinkCancel.setVisible( detailsVisible );
this.jXBusyLabel.setText( "22 %" );
}

@SuppressWarnings("unchecked")
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;

jXPanel1 = new org.jdesktop.swingx.JXPanel();
jXBusyLabel = new org.jdesktop.swingx.JXBusyLabel();
jProgressBar = new javax.swing.JProgressBar();
jXHyperlinkCancel = new org.jdesktop.swingx.JXHyperlink();

setOpaque(false);
setLayout(new java.awt.GridBagLayout());

jXPanel1.setOpaque(false);
jXPanel1.setLayout(new java.awt.GridBagLayout());

jXBusyLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
jXBusyLabel.setText("xx %");
jXBusyLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
jXBusyLabel.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.insets = new java.awt.Insets(20, 20, 0, 20);
jXPanel1.add(jXBusyLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 2;
gridBagConstraints.insets = new java.awt.Insets(0, 20, 20, 0);
jXPanel1.add(jProgressBar, gridBagConstraints);

jXHyperlinkCancel.setText("cancel");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 2;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 20, 20);
jXPanel1.add(jXHyperlinkCancel, gridBagConstraints);

add(jXPanel1, new java.awt.GridBagConstraints());
}

}
[/code]

The repaint on the glasspane components when the layerUI is in locked state seems to be the problem.

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

Hello Andre

First JXLayer is not supposed to be subclassed at all,
tomorrow I'll make this class final to make absolutely clear
It it LockableUI must be subclassed to provide your own functionality

Second what you call "a problem" is a trivial NullPointerException in your code,
as you can see it in your console

Your IssuePanel starts a thread in its consructor,
(it looks horrible, by the way)
and when the thread started the lockableUI is null,
because it is not initialized yet

this problem has nothing to do with JXLayer or LockableUI

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01
Points: 0

Ok, the NPE occurs by a "last minute" change on the post message forum without re-testing it in my IDE.

Sorry for the disappointment, but the NPE is not the problem that i try to reveal to you. i'm not an expert on JXLayer but i'am be able to handle an NPE...

Don't focus on the design (thread in ctor & co) since this dirty implementation is done only for complete a little sample and view the problem in a small code snippets like you asked to me.

For your last point, (subclassing JXLayer), it's sound bad to me.
I need to wrap a component on another one. Since i use JXlayer, it's seems natural to extends JXlayer, setup it correctly and return the JXLayer as the wrapped components.

Marking this class final seems to be a quickest shortcut that need to discuss in more details.

This code don't have this NPE bug anyway:

[code]
package org.divxdede.swing.busy.issue;

import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.JTree;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.ext.LockableUI;

/**
*
* @author André Sébastien
*/
public class IssueComponent extends JXLayer {

public IssueComponent( C view ) {
super(view);

IssuePanel panel = new IssuePanel();
setGlassPane( panel );
setUI( panel.lockableUI );
}

public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add( new IssueComponent( new JTree() ) );
frame.pack();
frame.setSize( new Dimension(300,150) );
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation( frame.EXIT_ON_CLOSE );
frame.setVisible(true);
}
}
[/code]

and

[code]
package org.divxdede.swing.busy.issue;

import javax.swing.SwingUtilities;
import org.jdesktop.jxlayer.plaf.ext.LockableUI;
import org.jdesktop.swingx.JXPanel;

public class IssuePanel extends JXPanel {

javax.swing.JProgressBar jProgressBar;
org.jdesktop.swingx.JXBusyLabel jXBusyLabel;
private org.jdesktop.swingx.JXHyperlink jXHyperlinkCancel;
private org.jdesktop.swingx.JXPanel jXPanel1;

boolean visible = false;
boolean detailsVisible = false;
LockableUI lockableUI = new LockableUI();

/** Creates new form BusyPanelImpl */
public IssuePanel() {
initComponents();
stateChanged();

Runnable doRun = new Runnable() {
public void run() {
sleep(1000);
visible = true;
stateChanged();

sleep(3000);
detailsVisible = true;
stateChanged();

sleep(3000);
detailsVisible = false;
stateChanged();

sleep(3000);
visible = false;
stateChanged();
}

private void sleep(long ms) {
try {
Thread.sleep(ms);
}
catch(InterruptedException ie) {
ie.printStackTrace();
}
}
};
Thread t = new Thread(doRun);
t.start();
}

/** Refresh the panel state from the current model state
*/
public void stateChanged() {
if( ! SwingUtilities.isEventDispatchThread() ) {
Runnable doRun = new Runnable() {
public void run() {
stateChanged();
}
};
SwingUtilities.invokeLater(doRun);
return;
}

/** ***********************************************
* IF YOU COMMENT THIS LINE, THE PROBLEM DISAPPEAR
* ***********************************************
*/
lockableUI.setLocked( visible );

this.jXBusyLabel.setVisible( visible );
this.jXBusyLabel.setBusy( visible );

this.jProgressBar.setVisible( detailsVisible );
this.jXHyperlinkCancel.setVisible( detailsVisible );
this.jXBusyLabel.setText( "22 %" );
}

@SuppressWarnings("unchecked")
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;

jXPanel1 = new org.jdesktop.swingx.JXPanel();
jXBusyLabel = new org.jdesktop.swingx.JXBusyLabel();
jProgressBar = new javax.swing.JProgressBar();
jXHyperlinkCancel = new org.jdesktop.swingx.JXHyperlink();

setOpaque(false);
setLayout(new java.awt.GridBagLayout());

jXPanel1.setOpaque(false);
jXPanel1.setLayout(new java.awt.GridBagLayout());

jXBusyLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
jXBusyLabel.setText("xx %");
jXBusyLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
jXBusyLabel.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.insets = new java.awt.Insets(20, 20, 0, 20);
jXPanel1.add(jXBusyLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 2;
gridBagConstraints.insets = new java.awt.Insets(0, 20, 20, 0);
jXPanel1.add(jProgressBar, gridBagConstraints);

jXHyperlinkCancel.setText("cancel");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 2;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 20, 20);
jXPanel1.add(jXHyperlinkCancel, gridBagConstraints);

add(jXPanel1, new java.awt.GridBagConstraints());
}

}
[/code]

PS: Sorry for my poor english by the way.

edit subclass question

Message was edited by: divxdede

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

Hello Andre

I am sorry, but when I run this test I don't see any painting problems

With the latest commit I made JXLayer class final,
extend LockableUI and use its installUI() method to set your own glassPane
and uninstallUI() to unset it

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01
Points: 0

If you don't have the problem, i will try with the last version of JXLayer.

With my version (downloaded yesterday on the official website) i have this result:

screenshot: http://z1.zod.fr/z/layer-ydt.html

I don't want to bother you anymore...

divxdede
Offline
Joined: 2007-09-01
Points: 0

I just downloaded the latest version of JXLayer including your JXLayer final hint.

After adapting the IssueComponent to Issue like here :

[code]
package org.divxdede.swing.busy.issue;

import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JTree;
import org.jdesktop.jxlayer.JXLayer;

/**
*
* @author André Sébastien
*/
public class Issue {

public static void main(String[] args) {
JXLayer comp = new JXLayer( new JTree() );

IssuePanel panel = new IssuePanel();
comp.setGlassPane( panel );
comp.setUI( panel.lockableUI );

JFrame frame = new JFrame();
frame.getContentPane().add( comp );
frame.pack();
frame.setSize( new Dimension(300,150) );
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation( frame.EXIT_ON_CLOSE );
frame.setVisible(true);
}
}
[/code]

The result was the same for me....
I have painting artifact when the LockableUI is locked.

With the lockable ui locked : http://z1.zod.fr/z/wrong-Udt.html (bugged)
Without the lockable ui locked : http://z1.zod.fr/z/right-Vdt.html (fine)

PS: i updated SwingX with the last Weekly build for ensure that the 0.9.3 version was not the source of the problem.

In all case, as long as JXLayer will be marked final, this api don't fit anymore all of my needs but i wish to know why you don't reproduce the bug...

Best Regards,
ANDRE Sébastien.

Message was edited by: divxdede

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

Hello Andre

You don't bother me my friend, I am ready to help JXLayer's users

I run your test with JDK 1.5 and JDK 1.6 - in both cases I didn't see the "busy effect" at all,
I saw only an empty progress bar with a label which reads "cancel"

Two mentioned components are shown and then hidden

If you see something different, it looks like a threading problem
(in your or SwingX code, can't say anything in particular)

By the way I can't imagine that final JXLayer class may not be fit for your needs,
if you look at the JXLayer demos you won't see a single example of subclassing this component

It is designed to only be used with a custom LayerUI
Just subclass LockableUI and set your glassPane it its installUI() method

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01
Points: 0

Exactly,

The sample show a busy label centered on the glasspane.
After few times, a progress bar and a label "cancel" appear below the busy label.

The container on the glasspane layer these childs with a centering policy.
The result is to move up the busy label when the progress bar and the cancel button appears.

In some case (layerUI locked) i keep a paint trace of the busy label at it's first position before to move up. And on some other case (layerUI not locked) i haven't this problem.

Whatever the sample is a dirty part, all ui events seems to be fired on the EDT and i can't figure where is the threading problem...

I don't know if you are able to see my screenshots posted here (the host server was down temporarily this morning but it seems to work now)

So now, i will trying to explain why i attempt to subclass JXLayer component.

My API has a design like it:
- Developper can give "busy" functionnality to any swing components (JComponent)
- It can enhance a component like it

[code]
JTree myTree = ....;
JBusyComponent jBusyTree = new JBusyComponent(myTree);
[/code]

The JBusyComponent as only 2 contracts that are:
- It's a Swing Component (IS-A JComponent)
- Enhance it with few and simple methods allowing to hold a BusyModel

[code]
public BusyModel getBusyModel();
public void setBusyModel( BusyModel model );
[/code]

It was a good deal for me to achieve theses 2 points by subclassing JXLayer.
I configure the JXLayer in the ctor with setUI et setGlassPane. If you preferer i can use installUI / uninstallUI but after regarding what setUI does, i was thinking that was no really difference.

Now with a JXLayer marked as final, the simplest way for me is subclass a container like JPanel (or directly JComponent) and adding the JXLayer inside. You force me to complexify my components hierarchy.

In fact, in swing, all JComponent are subclassable what even the component is complex. We can subclass a JTable that is not a trivial one.
In all case, subclassing an object is always the responsability of the developper.

Best regards,
ANDRE Sébastien.

Message was edited by: divxdede

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

Hello ANDRE

I saw your screenshots, but still don't have any ideas what might cause a problem

My comments for your notes about subclassing JXLayer:

I understand your API but I don't think that it is the best choice
There is a well-known rule of thumb for OOP:
"Prefer delegation to inheritance"
actually all Swing is built on this principle,
models and ui delegates are the main examples

All the core Swing components are not-final, that's true
but JXLayer is a very specific component
and doesn't have much in common with JTable or JTree
it is actually a wrapper which delegates every functionality to its ui

I strongly believe that JBusyComponent concept is less preferable than BusyLayerUI
which has getBusyModel()/setBusyModel() and works well for a regular JXLayer

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01
Points: 0

I understand your point of view concerning a BusyLayerUI instead of a JBusyComponent and i need to study this solution.

But the main goal on JBusyComponent was also to hide implementation used for achieve this feature. At this stage, JXLayer was just an implementation part. I can change in future without any impact on the API.

With your suggested design, my API becomes essentially a "JXLayer community stuff" and JXLayer becomes an entire part of my project as the core.

Maybe, i will go this way, it's need to study a little more.

PS: what we do with my unreproductible bug ? forgot it for now ?

Tanks for all,
Sébastien.

Message was edited by: divxdede

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

Hello Andre

> With your suggested design, my API becomes essentially a "JXLayer community stuff" and
> JXLayer becomes an entire part of my project as the core.

It sounds good :-)

> Maybe, i will go this way, it's need to study a little more.

Please have a look at the jxlayer demo package,
this is the way of using JXLayer was designed for

> what we do with my unreproductible bug ? forgot it for now ?

Could you
- make your test case as small as possible
(remove all components which are not relevant to see the problem)
- rewrite it to not start the thread from the ctor
- let me know about the result

Thanks
alexp

divxdede
Offline
Joined: 2007-09-01
Points: 0

mark it as answered since the setDirty resolve my pb.