javaswingjlayeredpane

Component goes back to origin once JLayeredComponent moves them to front layer


I have a program that moves the buttons to the front of the screen when you click and drag them. The problem is that once the layered pane adds the component to the layer above the other layers, it moves the component back to the origin of the JFrame.

package Control;

import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;

public class LayeredPaneTest {
static JLayeredPane layeredPane;

static int screenX;
static int screenY;

public static MouseListener getMouseListener(JComponent component) {
    MouseListener mListener = new MouseListener() {
        @Override
        public void mouseClicked(MouseEvent e) {}

        @Override
        public void mousePressed(MouseEvent e) {
            screenX = e.getXOnScreen();
            screenY = e.getYOnScreen();

            layeredPane.add(component, new Integer(1), 0); //this method is the one that makes the component go back to the origin
            //If I delete this method, the buttons no longer go back to the origin, but the layered obviously doesn't work
            //I've tried adding a setBounds method here for component, but it still goes back to the origin
        }

        @Override
        public void mouseReleased(MouseEvent e) {}

        @Override
        public void mouseEntered(MouseEvent e) {}

        @Override
        public void mouseExited(MouseEvent e) {}
    };
    return mListener;
}

public static MouseMotionListener getMouseMotionListener(JComponent component) {
    MouseMotionListener mmListener = new MouseMotionListener() {
        @Override
        public void mouseDragged(MouseEvent e) {
            int deltaX = e.getXOnScreen() - screenX;
            int deltaY = e.getYOnScreen() - screenY;
                
            component.setLocation(deltaX, deltaY);
        }

        @Override
        public void mouseMoved(MouseEvent e) {}

    };
    return mmListener;
}

private static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setLocation(0, 0);
    frame.setSize(400, 400);
    frame.setVisible(true);
    
    layeredPane = new JLayeredPane();
    
    JButton button1 = new JButton("JButton 1");
    button1.setBackground(Color.RED);
    button1.setSize(100, 50);
    button1.addMouseListener(getMouseListener(button1));
    button1.addMouseMotionListener(getMouseMotionListener(button1));
    layeredPane.add(button1, new Integer(0), 0);
    button1.setBounds(0, 0, button1.getWidth(), button1.getHeight());
    
    JButton button2 = new JButton("JButton 2");
    button2.setBackground(Color.BLUE);
    button2.setSize(100, 50);
    button2.addMouseListener(getMouseListener(button2));
    button2.addMouseMotionListener(getMouseMotionListener(button2));
    layeredPane.add(button2, new Integer(0), 0);
    button1.setBounds(100, 100, button2.getWidth(), button2.getHeight());
    
    JButton button3 = new JButton("JButton 3");
    button3.setBackground(Color.GREEN);
    button3.setSize(100, 50);
    button3.addMouseListener(getMouseListener(button3));
    button3.addMouseMotionListener(getMouseMotionListener(button3));
    layeredPane.add(button3, new Integer(0), 0);
    button1.setBounds(200, 200, button3.getWidth(), button3.getHeight());
    
    frame.add(layeredPane);
}

public static void main(String[] args) {
    //Schedule a job for the event-dispatching thread:
    //creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
   }); 
}
}

I've spent many hours trying to switch methods around, testing and whatnot, but the problem relies on the method layeredPane.add(component, new Integer(1), 0);. I'm very new to JLayeredPanes, so there must be something I'm missing.


Solution

  • The problem is not with the layeredPane.add() method, it is in the way that you calculate the new component location.

    Let's say that a button is a location so that when you click at position (200, 100) you press the button. At that moment you store screenX=200; screenY=100;.

    If you now move the mouse to (210, 100) you calculate the new position as deltaX = 210-200; and deltaY = 100 - 100;, which places the component in the top left corner.


    You probably should store the offset of the mouse relative to the component on the mousePressed event and adjust the component location for this offset on the mouseDragged event:

    static int offsetX;
    static int offsetY;
    
    public static MouseListener getMouseListener(JComponent component) {
        // [...]
        @Override
        public void mousePressed(MouseEvent e) {
            offsetX = e.getXOnScreen() - component.getX();
            offsetY = e.getYOnScreen() - component.getY();
    
            layeredPane.add(component, new Integer(1), 0);
        }
        // [...]
    }
    
    public static MouseMotionListener getMouseMotionListener(JComponent component) {
        MouseMotionListener mmListener = new MouseMotionListener() {
            @Override
            public void mouseDragged(MouseEvent e) {
                int posX = e.getXOnScreen() - offsetX;
                int posY = e.getYOnScreen() - offsetY;
                
                component.setLocation(posX, posY);
            }
            // [...]
        };
        return mmListener;
    }