Moving objects on GlassPane ignores setLocation()

I

Icarus

For a current project of mine I have to move some Labels around on the
GlassPane. For this, I am first putting the objects on the pane and
then move them to the right location using setLocation(). But the
setLocation() command is always ignored at first, until the next
MouseMotionEvent is intercepted.

Below is some sample code replicating the problem. Move the mouse in
the program window. Then use the mouse button, but don't move the
mouse. You will see the picture enter the window at the top, centered
vertically, even though the program sets the location to that of the
mouse cursor. If you now move the mouse, the program behaves correctly
again.

Do you have any idea how to correct this?


import java.awt.AWTEvent;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;


@SuppressWarnings("serial")
public class PickUpAndDrop2 extends JFrame{

// Use Singleton design pattern to provide easy access of the
relevant object
// to the listeners
private static PickUpAndDrop2 instance = new PickUpAndDrop2();

// The label to be moved around
JLabel label;

private PickUpAndDrop2(){
this.setSize(200, 200);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

// Create a JLabel to be moved around on the GlassPane
java.net.URL url = this.getClass().getResource("image.png");
ImageIcon image = new ImageIcon(url);
this.label = new JLabel(image);

// Register a listener to the label to be picked up on click
this.label.addMouseListener(this.createListener());

// Get the GlassPane and prepare it for display
JPanel glasspane = (JPanel)this.getGlassPane();
glasspane.setOpaque(false);

// Put the label on the window
this.add(this.label);
}

// Puts the label on the GlassPane
protected void pickUp(){
// Create a new GlassPane
JPanel glasspane = new JPanel();
this.setGlassPane(glasspane);
glasspane.setVisible(true);
glasspane.setOpaque(false);

// Register GlassPane as an "indirect" listener to MouseMotionEvents

Toolkit.getDefaultToolkit().addAWTEventListener(this.createAWTEventListener(),
AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK);

// Put the label on the GlassPane
glasspane.add(this.label);

// Put the label on the current location of the mouse cursor
Point position = this.getMousePosition();
this.moveLabel(position);
}

// Move the label around
protected void moveLabel(Point position){
this.label.setLocation(position);
}

// Create a listener for putting the component the click occured on
on the GlassPane
private MouseAdapter createListener(){

MouseAdapter listener = new MouseAdapter(){

public void mouseClicked(MouseEvent event) {
PickUpAndDrop2.instance.pickUp();
}
};

return listener;
}

// Intercepts Events and checks whether or not there are events of
interest for the GlassPane
// without actually being a listener
private AWTEventListener createAWTEventListener(){
AWTEventListener listener = new AWTEventListener(){

@Override
public void eventDispatched(AWTEvent event) {

if (event instanceof MouseEvent) {
MouseEvent me = (MouseEvent) event;

// In case of a MouseEvent, move the object that's currently on
the GlassPane
// to the location of the event
Point point;
if (me.getID() == MouseEvent.MOUSE_EXITED &&
me.getComponent() == PickUpAndDrop2.instance) {
point = null;
} else {
MouseEvent converted =
SwingUtilities.convertMouseEvent(me.getComponent(), me,
PickUpAndDrop2.instance.getGlassPane());
point = converted.getPoint();
}
PickUpAndDrop2.instance.moveLabel(point);
}

}

};

return listener;
}

public static void main(String[] args){
PickUpAndDrop2 frame = PickUpAndDrop2.instance;
frame.setVisible(true);
}
}


P.s.: This is working on the results of an earlier message (see
http://groups.google.com/group/comp.lang.java.programmer/browse_thread/thread/15f94143527b7745#),
but as it's a different problem, I am sending this as a separate
message. I hope this is okay.
 
J

John B. Matthews

[...]
Below is some sample code replicating the problem. Move the mouse in
the program window. Then use the mouse button, but don't move the
mouse. You will see the picture enter the window at the top, centered
vertically, even though the program sets the location to that of the
mouse cursor. If you now move the mouse, the program behaves correctly
again.

Do you have any idea how to correct this?

The top center placement is a feature of the (default) FlowLayout in the
glassPane. Change it too a GridLayout to see a different effect; set it
to null to preclude the effect.

[...]
// Create a new GlassPane
JPanel glasspane = new JPanel(new GridLayout(1, 1));
[...]

In the listener, set the point to something on MOUSE_EXITED to avoid an
NPE. Click on the label and drag the frame around to see the label move
relative to the frame:
// In case of a MouseEvent, move the object that's
// currently on the GlassPane to the location of the event
Point point;
if (me.getID() == MouseEvent.MOUSE_EXITED
&& me.getComponent() == PickUpAndDrop2.instance) {
point = new Point(0, 0);
} else {

I think you need a different transformation here. Also, this code needs
to distinguish between events from the label and the the underlying
frame:
MouseEvent converted =
SwingUtilities.convertMouseEvent(
me.getComponent(), me,
PickUpAndDrop2.instance.getGlassPane());
point = converted.getPoint();
}
PickUpAndDrop2.instance.moveLabel(point);
}

Have you looked at this?

<http://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.html
#glasspane>

Sorry to be vague, but this feature is new to me.

[...]
 
I

Icarus

The top center placement is a feature of the (default) FlowLayout in the
glassPane. Change it too a GridLayout to see a different effect; set it
to null to preclude the effect.

Is there a way to avoid/ciercumvent this effect altogether? I dotry to
move the component to the right location immediately after placement,
but the change is ignored or at least not drawn. Although it's the
same command that gets triggered by the MouseMotionEvents.


I did look at it, but somehow the old image didn't get deleted when it
was moved to a new location. And at this point, changing the behaviour
to overriding paintComponent won't do anymore, as it would break my
structure.

Sorry to be vague, but this feature is new to me.

As it's with me. -_- Nonetheless, do you (or someone else) have any
ideas especially onthe problem with the wrong starting location?
 
I

Icarus

I just tried to change the code back to the "overriding
paintComponent" approach and what the heck, it's working! After I've
spent a day trying this out, I'm doing practically the same thing and
it is working fine!

I am still curious how I could get the JLabel approach working, but at
least I don't have the pressure of a deadline on me, and so you don't
need to break your back over it to help me. For those who are
inerested, and if you don't mind the german names, I used this:


import javax.swing.*;

import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;


@SuppressWarnings("serial")
public class VersteckteGlassPane extends JPanel implements
AWTEventListener {
private final JFrame vaterfenster;
private Point altePosition = new Point();
private Point aktPosition = new Point();
private BufferedImage bild;

public VersteckteGlassPane(JFrame vaterfenster) {
super(null);
this.vaterfenster = vaterfenster;
setOpaque(false);
}

public void setPosition(Point aktPosition) {
this.altePosition = this.aktPosition;
this.aktPosition = aktPosition;
}

public void setBild(BufferedImage bild){
this.bild = bild;
}

public Rectangle getRepaintBereich() {
int x = (int)aktPosition.getX();
int y = (int)aktPosition.getY();

int x2 = (int)altePosition.getX();
int y2 = (int)altePosition.getY();

int breite = bild.getWidth();
int hoehe = bild.getHeight();

return new Rectangle(x, y, breite, hoehe).union(new
Rectangle(x2, y2, breite, hoehe));
}

protected void paintComponent(Graphics g) {
if (bild == null || !isVisible()) {
return;
}

Graphics2D g2 = (Graphics2D) g.create();

g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1.0f));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);

int x = (int) (aktPosition.getX());
int y = (int) (aktPosition.getY());

g2.drawImage(bild, x, y, (int)bild.getWidth(),
(int)bild.getHeight(), null);
}

public void eventDispatched(AWTEvent event) {

if ((event instanceof MouseEvent) && (this.bild != null)) {
MouseEvent me = (MouseEvent) event;

Point aktPosition;
if (me.getID() != MouseEvent.MOUSE_EXITED ||
me.getComponent() != vaterfenster) {
MouseEvent converted =
SwingUtilities.convertMouseEvent(me.getComponent(), me,
vaterfenster.getGlassPane());
aktPosition = converted.getPoint();

this.setPosition(aktPosition);
this.repaint(this.getRepaintBereich());
}
}
}

public void registriere(boolean registriere) {

if(registriere){
Toolkit.getDefaultToolkit().addAWTEventListener(this,
AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK);
}
else{
Toolkit.getDefaultToolkit().
removeAWTEventListener(this);
}
}
}
 
J

John B. Matthews

Icarus said:
Is there a way to avoid/ciercumvent this effect altogether? I dotry to
move the component to the right location immediately after placement,
but the change is ignored or at least not drawn. Although it's the
same command that gets triggered by the MouseMotionEvents.

I think the problem is that it's the same label on both panes: it's
alternately being redrawn on the glassPane and repainted on the frame's
contentPane.
I did look at it, but somehow the old image didn't get deleted when it
was moved to a new location. And at this point, changing the behaviour
to overriding paintComponent won't do anymore, as it would break my
structure.

I don't see a way around it: the same component can't be in two
locations at once.
As it's with me. -_- Nonetheless, do you (or someone else) have any
ideas especially on the problem with the wrong starting location?

Start with the GlassPaneDemo and request repaint for drag and pressed:
public void mouseDragged(MouseEvent e) {
redispatchMouseEvent(e, true);
}

public void mousePressed(MouseEvent e) {
redispatchMouseEvent(e, true);
}

Then tracking works perfectly:

protected void paintComponent(Graphics g) {
if (point != null) {
g.setColor(Color.red);
g.drawRect(point.x, point.y, 60, 20);
}
}

Of course, you want to drag a label, not just an outline. A LayeredPane
might work:

<http://java.sun.com/docs/books/tutorial/uiswing/components/layeredpane.h
tml?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top