Search code examples
javaswingpaintglasspanecontentpane

Swing: Paint over all other components and preserve events


What I am trying to do: draw a vertical line and a horizontal line that are perpendicular to each other and meet where the mouse points. A sort of cursor tracker.

My structure: JFrame -> CustomPanel -> other panels/components etc.

CustomPanel inherits from JPanel and it's set as my JFrame's ContentPane.

I tried to use a GlassPane, everything worked perfectly, but I want to keep my events, not disable them. I still want to be able to click buttons etc.

A relevant question is this Painting over the top of components in Swing?. Everything behaves as expected when I move my mouse where there are empty places in my CustomPanel, but it still doesn't paint over the other components.

In the image it should have continued painting over the button, but it stopped when I entered it and then resumed when I exited.

enter image description here

Code below.

public class Painter {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        JFrame frame = new JFrame();

        frame.setSize(600, 600);
        frame.setPreferredSize(new Dimension(600, 600));
        frame.setContentPane(new CustomPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

class CustomPanel extends JPanel {
    int x = 0;
    int y = 0;

    public CustomPanel() {

        addMouseListener(new AdapterImplementation(this));
        addMouseMotionListener(new AdapterImplementation(this));
        add(new JButton("TESTBTN"));
        setSize(new Dimension(600, 600));
        setPreferredSize(new Dimension(600, 600));
        setVisible(true);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.drawLine(0, y, getWidth(), y);
        g.drawLine(x, 0, x, getHeight());
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }
}

and my adapter:

public class AdapterImplementation extends MouseAdapter {
    CustomPanel pane;

    public AdapterImplementation(CustomPanel pane) {
        this.pane = pane;
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        pane.setX(x);
        pane.setY(y);
        pane.repaint();
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        System.out.println("MOUSE MOVED");
        int x = e.getX();
        int y = e.getY();
        pane.setX(x);
        pane.setY(y);
        pane.repaint();
    }
}

Solution

  • The problem here is that the MouseListeners are registered with your CustomPanel but not with the JButton so the latter does not process events from the listeners.

    Also, as you've seen, events to underlying components will be blocked when using GlassPane.

    A JLayeredPane could be used to as a topmost container to capture the MouseEvents using your current listeners.

    Note: Override paintComponent instead of paint for custom painting in Swing and remember to invoke super.paintComponent(g).