Home > Enterprise >  Fix overlapping MouseListeners in JFrame
Fix overlapping MouseListeners in JFrame

Time:09-17

I am running into an issue where it seems that the MouseListeners are overlapping each other.

In my application's GUI class, I added @camickr 's ComponentResizer class and registered my JFrame. When there are no subcomponents, or else, when I don't add other MouseListeners to the subcomponents, it works fine. But when I do so, the subcomponent seems to have the listener priority, and I am not able to resize the north border.

I tried the addAWTEventListener() method, like the following:

Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
            if (event instanceof MouseEvent) {
                MouseEvent e = (MouseEvent) event;
                if (e.getSource() == MainGUI.getMainFrame()) {
                    switch (e.getID()) {
                        case MouseEvent.MOUSE_CLICKED -> mouseClicked(e);
                        case MouseEvent.MOUSE_PRESSED -> mousePressed(e);
                        case MouseEvent.MOUSE_RELEASED -> mouseReleased(e);
                        case MouseEvent.MOUSE_ENTERED -> mouseEntered(e);
                        case MouseEvent.MOUSE_EXITED -> mouseExited(e);
                        case MouseEvent.MOUSE_DRAGGED -> mouseDragged(e);
                        case MouseEvent.MOUSE_MOVED -> mouseMoved(e);
                        default -> {}
                    }
                }
            }
        }, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);

But the results were very weird, with all the components in the frame being resizeable at the same time.

Here's an SSCCE of the problem:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MainGUI {

    private static JFrame mainFrame;
    private static JPanel contentPane;
    private static JPanel titleBar;

    private static ComponentResizer resizer = new ComponentResizer();

    public static void init() {
        mainFrame = new JFrame();
        contentPane = new JPanel();

        titleBar = new JPanel();
        titleBar.add(new JLabel("This is a title"));
        titleBar.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));

        // Problematic part. The resizer works correctly without it.
        titleBar.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("You clicked me!");
            }
        });

        contentPane.setLayout(new BorderLayout());
        contentPane.add(titleBar, BorderLayout.NORTH);

        mainFrame.setContentPane(contentPane);
        mainFrame.setPreferredSize(new Dimension(600, 400));
        mainFrame.setUndecorated(true);

        resizer.registerComponent(mainFrame);
    }

    public static void display() {
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(null);
        mainFrame.setEnabled(true);
        mainFrame.setVisible(true);
        mainFrame.requestFocus();
    }

    public static JFrame getMainFrame() {
        return mainFrame;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException |
                    IllegalAccessException | UnsupportedLookAndFeelException e) {
                e.printStackTrace();
            }
            MainGUI.init();
            MainGUI.display();
        });
    }
}

And here's ComponentResizer class:

http://www.camick.com/java/source/ComponentResizer.java

So the question is, is there a way to keep the JFrame's listeners "on top" of the subcomponents listeners?

CodePudding user response:

is there a way to keep the JFrame's listeners "on top" of the subcomponents listeners?

Not that I know of. Only a single component can receive the MouseEvent.

Swing will search the components from the bottom (of the parent / child component chain) to the top to find a component with a MouseListener and pass the event to that component. So the first component found with a listener will receive the event.

One solution is to provide a "wrapper" panel for your title bar with a Border.

For example:

    //  Create wrapper panel with a border

    JPanel wrapper = new JPanel(new BorderLayout());
    wrapper.setBorder( new EmptyBorder(5, 5, 5, 5) );
    wrapper.add(titleBar);

    contentPane.add(wrapper, BorderLayout.NORTH);
   //contentPane.add(titleBar, BorderLayout.NORTH);

Now your title bar will NOT extend to the edges of the frame so the MouseEvent will be passed to the frame when the mouse is within 5 pixels of the edge.

You can also remove the Border from the title bar and use a CompoundBorder on the wrapper panel if you want the LineBorder to fully extend to the edge of the frame.

Note you will likely be adding components to the CENTER of the BorderLayout as well. If any of these components have a MouseListener you will have the same problem. Depending on your requirments you might want to consider adding the EmptyBorder to the content pane of the frame. This way you effectively add a Border to the entire frame.

  • Related