Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MouseEvent lost in JScrollPane

This is the code I am using to show the issue which I am facing in another project.

I am not getting any line like this if I use JScrollPane as a wrapper for panel2. Why? I want to click on JscrollPane and got event printed as following.

java.awt.event.MouseEvent[MOUSE_CLICKED,(800,469),absolute(808,499),button=1,modifiers=Button1,clickCount=1] on javax.swing.JPanel[,0,0,934x612,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.LineBorder@cc0e01,flags=9,maximumSize=,minimumSize=,preferredSize=java.awt.Dimension[width=880,height=630]]

If now I change

panel1.add(pane);

to

panel1.add(panel2);

Then the message above got printed.

public class LostMouseEvent {

public static void main(String[] args) {
    new LostMouseEvent();
}

public LostMouseEvent() {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());

            JPanel panel1 = new JPanel();
            JPanel panel2 = new JPanel();
            JScrollPane pane = new JScrollPane(panel2);

            panel1.setPreferredSize(new Dimension(880, 630));
            panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
            panel2.setPreferredSize(new Dimension(840, 610));
            panel2.setBorder(BorderFactory.createLineBorder(Color.green));


            panel1.add(pane);
            frame.add(panel1);

            frame.pack();
            frame.setVisible(true);
            frame.setSize(950, 650);

            panel1.addMouseListener(new MyMouseListener());
        }
    });
}

private class MyMouseListener extends MouseAdapter {
    @Override
    public void mouseClicked (MouseEvent me) {
        System.out.println(me);
    }
}
}

UPD: In fact in my project there is more than just one panel2. Originally, I had panel1 and many panel2 inside. Then I wanted to wrap each panel2 with JScrollPane and started to face this problem.

I need to have only one MouseListener to minimize changes to the code.

like image 219
Nikolay Kuznetsov Avatar asked Jan 16 '23 01:01

Nikolay Kuznetsov


2 Answers

  • Use EDT for creation and manipulation of Swing components
  • Dont call setSize() rather call pack() before setting JFrame visible.
  • Dont call setPrefferedSize() rather override getPrefferedSize()

Your code works as expected, it will only print the message if panel1 is clicked, note panel1 is behind JScrollPane, thus anything outside the green border is panel1. To make it work for both the JScrollpane/panel2 and JPanel/panel1 simply add the MouseListener to BOTH of the required components:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class LostMouseEvent {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
        new LostMouseEvent();
            }
        });
    }

    public LostMouseEvent() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());

                JPanel panel1 = new JPanel() {

                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(880, 630);
                    }

                };
                JPanel panel2 = new JPanel() {

                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(840, 610);
                    }
                };
                JScrollPane pane = new JScrollPane(panel2);

                panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
                panel2.setBorder(BorderFactory.createLineBorder(Color.green));


                panel1.add(pane);
                frame.add(panel1);

                MouseListener ml=new MyMouseListener();

                //add mouse listener to panel1 and panel2
                panel1.addMouseListener(ml);
                panel2.addMouseListener(ml);

                //alternatively add to pane
                //pane.addMouseListener(ml);

                frame.pack();
                frame.setVisible(true);

            }
        });
    }

    private class MyMouseListener extends MouseAdapter {

        @Override
        public void mouseClicked(MouseEvent me) {
            System.out.println(me);
        }
    }
}

EDIT:

I personally would not recommend this, however,

To add a single listener to the JFrame that will capture all MouseEvents use Toolkit class and call addAWTEventListener like so:

Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
    @Override
    public void eventDispatched(AWTEvent awte) {//all mouse events will be processed here you will have to check for the mouse events you are interested in
    System.out.println(awte);
    }
}, AWTEvent.MOUSE_EVENT_MASK);//for Mouse events only

UPDATE 1:

You could also add the MouseListener to your JFrames glasspane via JFrame.getGlassPane().addMouseListener(ml) dont forget to set the glasspane visible after setting JFrame visible. This will allow you to only have to add a single Listener. See here:

...

MouseListener ml = new MyMouseListener();

//add mouse listener to panel1 and panel2
//panel1.addMouseListener(ml);
//panel2.addMouseListener(ml);

//alternatively add to pane
//pane.addMouseListener(ml);

frame.getGlassPane().addMouseListener(ml);

frame.pack();
frame.setVisible(true);

frame.getGlassPane().setVisible(true);

...

UPADTE 2:

The main reason for you having the problem of the MouseEvent getting lost in JScrollPane is because its a bug. See here.

The work around shown is:

public Test()
  {
    setUI(new javax.swing.plaf.metal.MetalScrollPaneUI(){
      public void installListeners(JScrollPane scrollPane){}
    });
    JPanel canvas = new JPanel();
    canvas.add( new JLabel("Test") );

    setViewportView( canvas );
    setVisible(true);
  }
like image 109
David Kroukamp Avatar answered Jan 17 '23 14:01

David Kroukamp


MouseEvent lost in JScrollPane

  • answer is very / quite simple, be sure that there isn't something about lost events, nor with JScrollPane,

  • Swing JComponent can firing event only if is there added proper Listener

  • you not added MouseListener to second JPanel,

  • this JPanel is placed into parent JPanel, this parent has added MouseListener then firing mouseEvent, sure in your case only outside of Bounds of 2nd JPanel added to this container

  • then 2nd JPanel is deepestComponentAt, and not possible fire event without redispatch coordinates from parent to child

  • you can to redispatch event programatically too,

like image 22
mKorbel Avatar answered Jan 17 '23 16:01

mKorbel