Java Error JPopupMenu

It looks like I found an error in java:

I need to create a JFrame using transparent background . And now I need to show JPopupMenu some user actions. This is normal when JPopupMenu is completely inside the JFrame . But when the JPopupMenu partially outside the JFrame , the item is not displayed.

SSCCE:

 public class PopupTest { public static void main(String[] a) { final JFrame frame = new JFrame(); frame.setSize(500, 500); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createLineBorder(Color.RED)); panel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { JPopupMenu menu = new JPopupMenu(); for (int i = 0 ; i < 10; i++) { menu.add(String.valueOf(i)); } menu.show(panel, e.getX(), e.getY()); } } }); frame.setContentPane(panel); frame.setUndecorated(true); frame.setBackground(new Color(50, 50, 50, 200)); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { frame.setVisible(true); } }); } } 

Does anyone know how to solve this?

PS: JDK 7u40, Win x64

+4
source share
3 answers

This is a bug in Oracle JDK 7 (it cannot be played in Open JDK 7, by the way).

To fix this, you can make a workaround (yes, this is just a workaround, there is no guarantee that it will not break with some Java update), so the window created for the pop-up menu will become opaque, because as soon as it appears, it will be displayed correctly. At least for now. Here's how to do it for Java version 7 and later:

 PropertyChangeListener propertyChangeListener = new PropertyChangeListener () { @Override public void propertyChange ( final PropertyChangeEvent evt ) { if ( evt.getNewValue () == Boolean.TRUE ) { // Retrieving popup menu window (we won't find it if it is inside of parent frame) final Window ancestor = getWindowAncestor ( popupMenu ); if ( ancestor != null && ancestor.getClass ().getCanonicalName ().endsWith ( "HeavyWeightWindow" ) ) { // Checking that parent window for our window is opaque, only then setting opacity final Component parent = ancestor.getParent (); if ( parent != null && parent instanceof Window && parent.getBackground ().getAlpha () == 0 ) { // Making popup menu window non-opaque ancestor.setBackground ( new Color ( 0, 0, 0, 0 ) ); } } } } private Window getWindowAncestor ( Component component ) { if ( component == null ) { return null; } if ( component instanceof Window ) { return ( Window ) component; } for ( Container p = component.getParent (); p != null; p = p.getParent () ) { if ( p instanceof Window ) { return ( Window ) p; } } return null; } }; popupMenu.addPropertyChangeListener ( "visible", propertyChangeListener ); 

You will have to make additional efforts if you also want to support JDK 6 versions because this code does not even compile on earlier versions of JDK (in earlier versions on Windows there are no set / getBackground methods).

+4
source

.

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.Painter; import javax.swing.SwingUtilities; import javax.swing.UIManager; public class PopupTest { private JFrame frame = new JFrame(); private JPanel panel = new JPanel(new BorderLayout()) { private static final long serialVersionUID = 1L; @Override public Dimension getPreferredSize() { return new Dimension(500, 500); } }; private JButton button = new JButton("Close me"); public PopupTest() { //panel.setOpaque(false); panel.setBorder(BorderFactory.createLineBorder(Color.RED)); panel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { JPopupMenu menu = new JPopupMenu(); for (int i = 0; i < 56; i++) {//only FHD display menu.add(String.valueOf(i)); } menu.show(panel, e.getX(), e.getY()); } } }); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); button.setOpaque(false); panel.add(button, BorderLayout.SOUTH); frame.setLocation(150, 150); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //frame.add(panel); frame.setContentPane(panel); frame.setUndecorated(true); frame.pack(); frame.setBackground(new Color(150, 50, 50, 200)); frame.setVisible(true); } public static void main(String[] a) { try { for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(laf.getName())) { UIManager.setLookAndFeel(laf.getClassName()); UIManager.getLookAndFeelDefaults().put("PopupMenu[Enabled].backgroundPainter", new FillPainter(new Color(127, 255, 191))); UIManager.getLookAndFeelDefaults().put("text", new Color(255, 0, 0)); //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); //UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); //UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"); } } } catch (Exception e) { e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new PopupTest(); } }); } } class FillPainter implements Painter<JComponent> { private final Color color; FillPainter(Color c) { color = c; } @Override public void paint(Graphics2D g, JComponent object, int width, int height) { g.setColor(color); g.fillRect(0, 0, width - 1, height - 1); } } 
+1
source

Thanks to Mikle Garin for a great solution, you really helped me solve a similar problem! I would like to share my solution - based on Mikle - with a slight difference, which was significant in my case.

What I was looking for: transparent, undecorated windows behind JPopupMenu (my custom pop-ups are shown with a fancy speech border, so the window behind it should be invisible).

One thing that didn’t work very well with PropertyChangeListener: the view of the window is adjusted AFTER the window is displayed on the screen. On Mac OS X 10.10 with a java 8 window, a white background and a thin border (default L & F) are displayed behind the tooltip, and then it will be adjusted for a while (approximately 0.1 - 0.3 seconds) later. Pretty annoying.

After some time, when I put the setup code, I came up with the following simple solution: adjust the RIGHT window AFTER the menu is added to the window and BEFORE the window is displayed. The following example shows how to extend JPopupMenu to achieve this goal:

 import java.awt.Color; import java.awt.Window; import javax.swing.JPopupMenu; import javax.swing.Popup; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; import javax.swing.plaf.PopupMenuUI; public class MyPopup extends JPopupMenu { public MyPopup() { super(); //... setUI(new MyPopupMenuUI()); } //... private void adjustHeavyWeightWindowIfThereIsOne() { // Retrieve popup menu window // on Windows we won't find it if this popup is inside of parent frame // on Mac we may find it even when DefaultLightWeightPopupEnabled is set to true final Window ancestor = SwingUtilities.getWindowAncestor(MyPopup.this); if (ancestor != null && ancestor.getClass().getCanonicalName().endsWith("HeavyWeightWindow")) { adjustWindowAppearance(ancestor); } } private void adjustWindowAppearance(Window w) { w.setBackground(new Color(0, 0, 0, 0)); if (w instanceof RootPaneContainer) { ((RootPaneContainer)w).getRootPane().setBorder(null); ((RootPaneContainer)w).getRootPane().setBackground(new Color(0, 0, 0, 0)); } } class MyPopupMenuUI extends PopupMenuUI { public Popup getPopup(JPopupMenu popup, int x, int y) { Popup toreturn = super.getPopup(popup, x, y); adjustHeavyWeightWindowIfThereIsOne(); return toreturn; } } } 

In fact, the JPopupMenu extension is not required, but setting up a custom PopupMenuUI that causes the adjustment is important. Window customization code can be easily changed to meet specific needs and can be ported to a custom PopupMenuUI.

0
source

All Articles