4

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.

Nikolay Kuznetsov
  • 9,467
  • 12
  • 55
  • 101
  • if you click outside the green border it works like it should, what are you trying to achieve – David Kroukamp Nov 13 '12 at 07:57
  • I want to click inside the green border and got the event printed. Why event from JScrollPane does not bubble up to JPanel panel1? – Nikolay Kuznetsov Nov 13 '12 at 07:58
  • you not added MouseListener to the second JPanel, for relative coordinated to use SwingUtilities#convert.... (most efficient from MouseMotionListener) – mKorbel Nov 13 '12 at 08:01
  • @mKorbel I want to add only one MouseListener to panel1 and process all events using it. Is this possible? – Nikolay Kuznetsov Nov 13 '12 at 08:04
  • 1
    what you want to save, one code line or :-), no idea whats your goal, but AWTEventListener doesn't returns relative coordinates betweens parent and child and vice versa, have look at Swing Utilities – mKorbel Nov 13 '12 at 08:23
  • @mKorbel, please see my update. I have already much in MouseListener assuming there is only one the system. So I don't understand why MouseEvent got lost in JScrollPane. – Nikolay Kuznetsov Nov 13 '12 at 10:06
  • I'll to send and longer comment as comment – mKorbel Nov 13 '12 at 10:30

2 Answers2

5
  • 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);
  }
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • 1
    not sure if isn't there long timed bug to use JRootPane rootPane = SwingUtilities.getRootPane(frame); rootPane.setGlassPane(myGlassPane); – mKorbel Nov 13 '12 at 09:48
  • +1 Not sure either.. guess OP can test it out depending, but I do find it irritating to have to call `setVisible` on glasspane after jframe visible. – David Kroukamp Nov 13 '12 at 09:51
  • @DavidKroukamp, yeah, I would like to keep only one MouseListener since it has much code and I want not to refactor it a lot considering several of them. – Nikolay Kuznetsov Nov 13 '12 at 10:12
  • Actually, I don't understand why JScrollPane does consume event without delivering it to panel1. If the event is skipped by pane and delivered to panel1 it would be the best for me. – Nikolay Kuznetsov Nov 13 '12 at 10:13
  • @DavidKroukamp, this does not make clearer to me. In the code without JScrollPane there is panel2 inside panel1 and I click on panel2 and event got delivered to panel1. – Nikolay Kuznetsov Nov 13 '12 at 12:05
  • 1
    @NikolayKuznetsov its seems to be a bug: https://forums.oracle.com/forums/thread.jspa?threadID=1362237 – David Kroukamp Nov 13 '12 at 12:21
  • @DavidKroukamp, seems to be well-known bug, but not fixed yet for some reasons. – Nikolay Kuznetsov Nov 13 '12 at 12:24
4

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,

mKorbel
  • 109,525
  • 20
  • 134
  • 319