Question

I have a JFrame and a bunch of JComponents on top of the JFrame. I need to make use of the JGlassPane and I used this implementation to set it up.

   JPanel glass = new JPanel();
   frame.setGlassPane(glass);
   glass.setVisible(true);
   glass.setOpaque(false);

After doing so I can't select any JButtons or other JComponents under the JGlassPane.

Is there a way to have only the components on the GlassPane selectable while still having the ability to select components under the GlassPane?

Edit I forgot to mention (not knowing this would be relevant) that I did attach both a MouseListener and a MouseMotionListener to the glass pane. Is there a way to pass the Mouse Events to other components and only use them when needed?

Was it helpful?

Solution

Make your mouseListener dispatch the events it doesn't want to handle.

The example code below is mixed, using the nice SSCCE by @whiskeyspider and the tutorial (BTW: looking into the tutorial is a good starter for solving problems :-)

ml = new MouseListener() {
    @Override
    public void mousePressed(MouseEvent e) {
        dispatchEvent(e);
    }
    // same for all methods
    // ....

    private void dispatchEvent(MouseEvent e) {
        if (isBlocked)
            return;
        Point glassPanePoint = e.getPoint();
        Container container = frame.getContentPane();
        Point containerPoint = SwingUtilities.convertPoint(glass,
                glassPanePoint, container);

        if (containerPoint.y < 0) { // we're not in the content pane
            // Could have special code to handle mouse events over
            // the menu bar or non-system window decorations, such as
            // the ones provided by the Java look and feel.
        } else {
            // The mouse event is probably over the content pane.
            // Find out exactly which component it's over.
            Component component = SwingUtilities.getDeepestComponentAt(
                    container, containerPoint.x, containerPoint.y);

            if (component != null) {
                // Forward events to component below
                Point componentPoint = SwingUtilities.convertPoint(
                        glass, glassPanePoint, component);
                component.dispatchEvent(new MouseEvent(component, e
                        .getID(), e.getWhen(), e.getModifiers(),
                        componentPoint.x, componentPoint.y, e
                                .getClickCount(), e.isPopupTrigger()));
            }
        }
    }
};

glass.addMouseListener(ml);
glassButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent ae) {
        if (isBlocked) {
            // glass.removeMouseListener(ml);
            glassButton.setText("Block");
        } else {
            // ml = new MouseAdapter() { };
            // glass.addMouseListener(ml);
            glassButton.setText("Unblock");
        }

        isBlocked = !isBlocked;
    }
});

OTHER TIPS

Here's an example of a JButton in the glasspane which when clicked will toggle capturing mouse events (can't click Test button).

public class Test
{
    private static boolean isBlocked = false;
    private static MouseListener ml;

    public static void main(String[] args)
    {
        JButton button = new JButton("Test");
        button.setPreferredSize(new Dimension(100, 100));

        final JButton glassButton = new JButton("Block");

        JPanel panel = new JPanel();
        panel.add(button);

        final JPanel glass = new JPanel();
        glass.setOpaque(false);
        glass.add(glassButton);

        final JFrame frame = new JFrame();
        frame.setGlassPane(glass);
        glass.setVisible(true);
        frame.setContentPane(panel);
        frame.pack();
        frame.setVisible(true);

        glassButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                if (isBlocked) {
                    glass.removeMouseListener(ml);
                    glassButton.setText("Block");
                } else {
                    ml = new MouseAdapter() { };
                    glass.addMouseListener(ml);
                    glassButton.setText("Unblock");
                }

                isBlocked = !isBlocked;
            }
        });
    }
}

Another solution without interrupt the Swing event chain, is to use a AWTEventListener instead. I've implemented a BetterGlassPane to be used instead of the regular JPanel glass pane, like so:

JFrame frame = ...; // any JRootPane will do...
BetterGlassPane glass = new BetterGlassPane(frame);

This does also work the "traditional" way, as shown in your question:

JPanel glass = new BetterGlassPane();
frame.setGlassPane(glass);    
glass.setRootPane(frame);

Hope this helps. Feel free to clone the project on GitHub or use the Maven dependency:

<dependency>
  <groupId>lc.kra.swing</groupId>
  <artifactId>better-glass-pane</artifactId>
  <version>0.1.3</version>
</dependency>
<repositories>
  <repository>
    <id>better-glass-pane-mvn-repo</id>
    <url>https://raw.github.com/kristian/better-glass-pane/mvn-repo/</url>
    <snapshots>
      <enabled>true</enabled>
      <updatePolicy>always</updatePolicy>
    </snapshots>
  </repository>
</repositories>

Short, Self Contained, Correct, Example (SSCCE, without obligatory Maven dependency):

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import lc.kra.swing.BetterGlassPane;

public class TestBetterGlassPane {
    public static void main(String[] args) {
        final JFrame frame = new JFrame("BetterGlassPane Test");
        frame.setLayout(null);
        frame.setSize(400,300);
        frame.setResizable(false);
        frame.setLocationByPlatform(true);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        BetterGlassPane glassPane = new BetterGlassPane(frame.getRootPane()) {
            private static final long serialVersionUID = 1L;
            @Override protected void paintComponent(Graphics graphics) {
                super.paintComponent(graphics);
                graphics.setColor(Color.BLACK);
                graphics.drawRect(20,160,360,50);
                graphics.setFont(graphics.getFont().deriveFont(Font.BOLD));
                graphics.drawString("I'm the glass pane, click me!",120,190);
            }
        };
        glassPane.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent event) {
                if(new Rectangle(20,180,360,50).contains(event.getPoint()))
                    JOptionPane.showMessageDialog(frame,"I'm the glass pane!");
            }
        });
        glassPane.setLayout(null);

        ActionListener defaultActionListener = new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                JOptionPane.showMessageDialog(frame,
                    ((JButton)event.getSource()).getText());
            }
        };

        JButton frameButton = new JButton("I'm on the frame!");
        frameButton.addActionListener(defaultActionListener);
        frameButton.setBounds(20,20,360,50);
        frame.add(frameButton);

        JButton glassPaneButton = new JButton("I'm on the glass pane!");
        glassPaneButton.addActionListener(defaultActionListener);
        glassPaneButton.setBounds(20,90,360,50);
        glassPane.add(glassPaneButton);

        frame.setVisible(true);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top