Moving objects on GlassPane ignores setLocation()

Discussion in 'Java' started by Icarus, Aug 1, 2008.

  1. Icarus

    Icarus Guest

    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.
    Icarus, Aug 1, 2008
    #1
    1. Advertising

  2. In article
    <>,
    Icarus <> wrote:

    [...]
    > 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.

    [...]

    --
    John B. Matthews
    trashgod at gmail dot com
    home dot woh dot rr dot com slash jbmatthews
    John B. Matthews, Aug 2, 2008
    #2
    1. Advertising

  3. Icarus

    Icarus Guest

    > 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.


    > Have you looked at this?
    >
    > <http://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.html
    > #glasspane>


    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?
    Icarus, Aug 2, 2008
    #3
  4. Icarus

    Icarus Guest

    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);
    }
    }
    }
    Icarus, Aug 2, 2008
    #4
  5. In article
    <>,
    Icarus <> wrote:

    > > 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 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.

    > > Have you looked at this?
    > >
    > > <http://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.html
    > > #glasspane>

    >
    > 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.

    > > 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 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?

    --
    John B. Matthews
    trashgod at gmail dot com
    home dot woh dot rr dot com slash jbmatthews
    John B. Matthews, Aug 2, 2008
    #5
  6. Icarus

    Icarus Guest

    Thank you. I'll fiddle with this some more once I am finished with my
    project.
    Icarus, Aug 3, 2008
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Bryan R. Meyer

    Glasspane and Saving Graphics

    Bryan R. Meyer, Sep 13, 2003, in forum: Java
    Replies:
    8
    Views:
    3,086
    Bryan R. Meyer
    Sep 16, 2003
  2. Wolfgang
    Replies:
    1
    Views:
    1,094
  3. Jack

    setLocation does not work

    Jack, Nov 4, 2005, in forum: Java
    Replies:
    9
    Views:
    9,696
    Andrew Thompson
    Nov 5, 2005
  4. Jack

    setLocation (AWT)

    Jack, Nov 5, 2005, in forum: Java
    Replies:
    2
    Views:
    5,112
  5. asd
    Replies:
    3
    Views:
    7,632
    Knute Johnson
    Feb 7, 2007
Loading...

Share This Page