3

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?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Sammy Guergachi
  • 1,986
  • 4
  • 26
  • 52

3 Answers3

6

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;
    }
});
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • @Epicmaster GlassPane can to consume only MouseEvent in this case, have to add KeyListener too, otherwise all events from KeyBoard will be re_dispatched without any restrictions :-) – mKorbel Nov 07 '12 at 12:39
2

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;
            }
        });
    }
}
martinez314
  • 12,162
  • 5
  • 36
  • 63
2

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);
    }
}
Kristian Kraljic
  • 826
  • 12
  • 11