Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Expanding on awheel's advice, I've written the following example which uses Swing's glass pane functionality. The approach is a bit messy, but that's not uncommon when you're trying something moderately advanced in Swing.</p> <p>The idea is to display a transparent overlay panel (a glass pane covering the entire window contents) when one clicks the button, and dispose of it when the user either clicks anywhere in the window or presses a key.</p> <p>On top of this glass pane, I display another JPanel ("popup") and try to position it just above the button that triggers its visibility.</p> <p>This approach has one limitation you original dialog-based solution doesn't: whatever's drawn on top of the glass pane must fit inside the frame's content area (after all, it's not a window). That's why I in the code below do some adjustments to ensure that popup&lt;'s coordinates are within the content pane's bounds (otherwise the JLabel would simply be cropped at the frame's edges).</p> <p>It also has the limitation that mouse presses caught by the glass pane aren't delegated to any underlying components. So if you click a button while the glass pane is visible, the glass pane will go away but also consume the click, and the button you thought you clicked will not react. It is possible to go around this if one wishes, but then it gets even messier and I wanted to keep my example relatively simple. :-)</p> <pre>import java.awt.Color; import java.awt.Container; import java.awt.FlowLayout; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRootPane; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; public class GlassPaneTest extends JFrame { public static class PropertiesButton extends JButton { /** The currently displayed glass pane. * Should be null if nothing is displayed. */ private JPanel theGlassPane; /** Root pane of connected window. Used to attach the glass pane. */ private final JRootPane rootPane; /** Content pane of the connected window. Used for coordinate calculation. */ private final Container contentPane; /* A "key hook" that allows us to intercept any key press when the glass pane is visible, * so we can hide the glass pane. */ private final KeyEventDispatcher keyHook = new KeyEventDispatcher() { public boolean dispatchKeyEvent(KeyEvent e) { if (theGlassPane == null || e.getID() != KeyEvent.KEY_PRESSED) { return false; } setGlassPaneVisible(false); return true; } }; public PropertiesButton(Window parentWindow) { if (!(parentWindow instanceof JFrame || parentWindow instanceof JDialog)) { throw new IllegalArgumentException("only JFrame or JDialog instances are accepted"); } if (parentWindow instanceof JDialog) { rootPane = ((JDialog) parentWindow).getRootPane(); contentPane = ((JDialog) parentWindow).getContentPane(); } else { rootPane = ((JFrame) parentWindow).getRootPane(); contentPane = ((JFrame) parentWindow).getContentPane(); } addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setGlassPaneVisible(theGlassPane == null); } }); } private JPanel createGlassPane() { // Create the glass pane as a transparent, layout-less panel // (to allow absolute positioning), covering the whole content pane. // Make it go away on any mouse press. JPanel gp = new JPanel(); gp = new JPanel(); gp.setOpaque(false); gp.setLayout(null); gp.setBounds(contentPane.getBounds()); gp.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { setGlassPaneVisible(false); } }); // Create the "popup" - a component displayed on the transparent // overlay. JPanel popup = new JPanel(); popup.setBorder(new CompoundBorder( new BevelBorder(BevelBorder.RAISED), new EmptyBorder(5, 5, 5, 5))); popup.setBackground(Color.YELLOW); popup.add(new JLabel("Some info for \"" + getText() + "\".")); // Needed since the glass pane has no layout manager. popup.setSize(popup.getPreferredSize()); // Position the popup just above the button that triggered // its visibility. Point buttonLocationInContentPane = SwingUtilities.convertPoint(this, 0, 0, contentPane); int x = buttonLocationInContentPane.x; int horizOverlap = x + popup.getWidth() - contentPane.getWidth(); if (horizOverlap > 0) { x -= horizOverlap; } int y = buttonLocationInContentPane.y - popup.getHeight(); if (y &lt; 0) { y = 0; } popup.setLocation(x, y); gp.add(popup); return gp; } private void setGlassPaneVisible(boolean visible) { KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); if (visible) { theGlassPane = createGlassPane(); rootPane.setGlassPane(theGlassPane); theGlassPane.setVisible(true); kfm.addKeyEventDispatcher(keyHook); } else { theGlassPane.setVisible(false); kfm.removeKeyEventDispatcher(keyHook); theGlassPane = null; } } } // A simple test program public GlassPaneTest() { setTitle("A glass pane example"); setLayout(new FlowLayout(FlowLayout.CENTER)); for (int i = 1; i &lt;= 10; ++i) { PropertiesButton pb = new PropertiesButton(this); pb.setText("Properties button " + i); add(pb); } setSize(400, 300); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame f = new GlassPaneTest(); f.setDefaultCloseOperation(EXIT_ON_CLOSE); f.setVisible(true); } }); } } </pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload