How can i make a JPopupMenu transparent?
Asked Answered
O

4

6

I'd like to customize the look of JPopupMenu so i made a custom class extending the JPopupMenu class on i overrode the paintComponent method as i would do for any component i need to customize.

public class CustomPopupMenu extends JPopupMenu {

    @Override
    public paintComponent(Graphics g) {
        //custom draw
    }
}

The only problem i have right know is that i'm not able to make the JPopupMenu transparent. I though setOpaque(false) would be enough, i was wrong.

How can i make a JPopupMenu transparent?

Observation answered 9/10, 2012 at 13:21 Comment(5)
never tried, but I never heard about any issue with Translucent WindowsManchu
@Manchu a jpopupmenu is a window?Observation
sorry my endless lazyness, please see ma answer here :-)Manchu
!) What is the point of making a JPopupMenu transparent? If your answer is 'to make my app. a "Killer App."' then my advice is to apply your expertise to things other than futzing with the look and feel. Because that does not make a 'killer app.'. 2) For better help sooner, post an SSCCE.Spoliation
And i don't get the point of your comment. I need to make a jpopupmenu transparent to draw rounded borders.Observation
K
3

The problem with a popup menu is that it may be realized as a top-level container (Window), and a window is opaque, no matter what value you set with setOpaque(), its opaque. But windows can be made translucent, too.

You can hack it by forcing the use of a heavyweight popup and brutally altering its opacity. Try this as a starting base for experiments (Java7):

import java.awt.Window;

import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

public class TranslucentPopup extends JPopupMenu {

    {
        // need to disable that to work
        setLightWeightPopupEnabled(false);
    }

    @Override
    public void setVisible(boolean visible) {
        if (visible == isVisible())
            return;
        super.setVisible(visible);
        if (visible) {
            // attempt to set tranparency
            try {
                Window w = SwingUtilities.getWindowAncestor(this);
                w.setOpacity(0.667F);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

Note that it will not make submenus translucent!

Karrikarrie answered 9/10, 2012 at 14:17 Comment(3)
hmm .. why reflection? SwingUtilities.getWindowAncestor(this) should be good enough to get hold of the containing windowJurisconsult
Good point. I hacked the reflection part while I was experimenting when I still hadn't put in setLightWeightPopupEnabled(false), at that point I got the effect that the parent window became translucent. Will update the code. Thanks.Karrikarrie
for subMenus, do the same for a custom JMenu: override setPopupMenuVisible and force the popup's windowAncestor to be translucentJurisconsult
M
4

jpopupmenu is a window?

yes JPopup is container, for example

enter image description here

code for Java6 (have to change imports for Java7)

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

public class TranslucentWindow extends JFrame {

    private static final long serialVersionUID = 1L;
    private JMenuItem m_mniInsertRow;
    private JMenuItem m_mniInsertScrip;
    private JMenuItem m_mniDeleterRow;
    private JMenuItem m_mniDeleteExpiredScrip;
    private JMenuItem m_mniSetAlert;

    public TranslucentWindow() {
        super("Test translucent window");
        setLayout(new FlowLayout());
        add(new JButton("test"));
        add(new JCheckBox("test"));
        add(new JRadioButton("test"));
        add(new JProgressBar(0, 100));
        JPanel panel = new JPanel() {

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 300);
            }
            private static final long serialVersionUID = 1L;

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(Color.red);
                g.fillRect(0, 0, getWidth(), getHeight());
            }
        };
        panel.add(new JLabel("Very long textxxxxxxxxxxxxxxxxxxxxx "));
        add(panel);
        final JPopupMenu popupMenu = new JPopupMenu();
        m_mniInsertRow = new JMenuItem("Insert a Row");
        m_mniInsertRow.setOpaque(false);
        m_mniInsertScrip = new JMenuItem("Insert a Scrip");
        m_mniInsertScrip.setOpaque(false);
        m_mniDeleterRow = new JMenuItem("Delete a Row");
        m_mniDeleterRow.setOpaque(false);
        m_mniDeleteExpiredScrip = new JMenuItem("Delete a Expired Scrip");
        m_mniDeleteExpiredScrip.setOpaque(false);
        m_mniSetAlert = new JMenuItem("Set Alert");
        m_mniSetAlert.setOpaque(false);
        popupMenu.add(m_mniInsertRow);
        popupMenu.add(m_mniInsertScrip);
        popupMenu.addSeparator();
        popupMenu.add(m_mniDeleterRow);
        popupMenu.add(m_mniDeleteExpiredScrip);
        popupMenu.add(new JSeparator());
        popupMenu.add(m_mniSetAlert);
        panel.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    int x = e.getX();
                    int y = e.getY();
                    popupMenu.show(e.getComponent(), x, y);
                }
            }
        });
        setSize(new Dimension(400, 300));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        JFrame.setDefaultLookAndFeelDecorated(true);
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                Window w = new TranslucentWindow();
                w.setVisible(true);
                com.sun.awt.AWTUtilities.setWindowOpacity(w, 0.7f);
            }
        });
    }
}

EDIT

interesting support for Translucency and Nimbus L&F, especially Painter reproducing quite correct Color (Gradiend too on movement over the screen), ligth version with important changes for JPopupMenu, still from Java6

image

enter image description here

from code

import com.sun.java.swing.Painter;
import java.awt.*;
import javax.swing.*;

public class TranslucentWindow extends JFrame {

    private static final long serialVersionUID = 1L;
    private JPopupMenu popupMenu = new JPopupMenu();
    private JMenuItem m_mniInsertRow = new JMenuItem("Insert a Row");
    private JMenuItem m_mniInsertScrip = new JMenuItem("Delete a Row");
    private JMenuItem m_mniDeleterRow = new JMenuItem("Insert a Scrip");
    private JMenuItem m_mniDeleteExpiredScrip = new JMenuItem("Delete a Expired Scrip");
    private JMenuItem m_mniSetAlert = new JMenuItem("Set Alert");

    public TranslucentWindow() {
        super("Test translucent window");
        setLayout(new FlowLayout());
        add(new JButton("test"));
        add(new JCheckBox("test"));
        add(new JRadioButton("test"));
        add(new JProgressBar(0, 100));
        JPanel panel = new JPanel() {

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 300);
            }
            private static final long serialVersionUID = 1L;

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(Color.red);
                g.fillRect(0, 0, getWidth(), getHeight());
            }
        };
        panel.add(new JLabel("Very long textxxxxxxxxxxxxxxxxxxxxx "));
        panel.setComponentPopupMenu(popupMenu);
        add(panel);
        m_mniInsertRow.setOpaque(false);
        m_mniInsertScrip.setOpaque(false);
        m_mniDeleterRow.setOpaque(false);
        m_mniDeleteExpiredScrip.setOpaque(false);
        m_mniSetAlert.setOpaque(false);
        popupMenu.add(m_mniInsertRow);
        popupMenu.add(m_mniInsertScrip);
        popupMenu.addSeparator();
        popupMenu.add(m_mniDeleterRow);
        popupMenu.add(m_mniDeleteExpiredScrip);
        popupMenu.add(new JSeparator());
        popupMenu.add(m_mniSetAlert);
        setSize(new Dimension(400, 300));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        try {
            for (UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    UIManager.getLookAndFeelDefaults().put("nimbusOrange", (new Color(127, 255, 191)));
                    UIManager.getLookAndFeelDefaults().put("PopupMenu[Enabled].backgroundPainter",
                            new FillPainter(new Color(127, 255, 191)));

                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
        } catch (InstantiationException ex) {
        } catch (IllegalAccessException ex) {
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        }
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                Window w = new TranslucentWindow();
                w.setVisible(true);
                com.sun.awt.AWTUtilities.setWindowOpacity(w, 0.7f);
            }
        });
    }

    static 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);
        }
    }
}
Manchu answered 9/10, 2012 at 13:57 Comment(5)
interesting that the popup kind-of inherits the window's transparency (as long as no part of it is outside the main window)Jurisconsult
btw: please do me a favour and edit the code to use setComponentPopupMenu (instead of opening on mouseReleased, doing so should be banned from all code!)Jurisconsult
@Jurisconsult When the popup is entirely within the window, its realized as a simple JPanel inside the windows component hierarchy. Stumbled over it when trying to hack my solution. If setLightWeightPopupEnabled(false) is used, the popup will be consistently realized in a window. Probably this is an optimization left over from early Swing days when it was famous for being slow...Karrikarrie
@Jurisconsult for your endless pleasure to read the """correct""" answers on this forum, I'll do thatManchu
@Karrikarrie thanks for your question, learning item of day, Nimbus with Painter and Translucency support on WinXP / Java6, heloooo JavaFX can you heard me, one more year without you :-)Manchu
K
3

The problem with a popup menu is that it may be realized as a top-level container (Window), and a window is opaque, no matter what value you set with setOpaque(), its opaque. But windows can be made translucent, too.

You can hack it by forcing the use of a heavyweight popup and brutally altering its opacity. Try this as a starting base for experiments (Java7):

import java.awt.Window;

import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

public class TranslucentPopup extends JPopupMenu {

    {
        // need to disable that to work
        setLightWeightPopupEnabled(false);
    }

    @Override
    public void setVisible(boolean visible) {
        if (visible == isVisible())
            return;
        super.setVisible(visible);
        if (visible) {
            // attempt to set tranparency
            try {
                Window w = SwingUtilities.getWindowAncestor(this);
                w.setOpacity(0.667F);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

Note that it will not make submenus translucent!

Karrikarrie answered 9/10, 2012 at 14:17 Comment(3)
hmm .. why reflection? SwingUtilities.getWindowAncestor(this) should be good enough to get hold of the containing windowJurisconsult
Good point. I hacked the reflection part while I was experimenting when I still hadn't put in setLightWeightPopupEnabled(false), at that point I got the effect that the parent window became translucent. Will update the code. Thanks.Karrikarrie
for subMenus, do the same for a custom JMenu: override setPopupMenuVisible and force the popup's windowAncestor to be translucentJurisconsult
V
2

You don't have to extend JPopupMenu class, just make your menu non-opaque and then make the JMenuItems transparent instead (and non-opaque).

public class CustomMenuItem extends JMenuItem {
        public void paint(Graphics g) { 
                Graphics2D g2d = (Graphics2D) g.create(); 
                g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f)); 
                super.paint(g2d); 
                g2d.dispose(); 
        } 
}

enter image description here

OR, do the opposite, extend JPopupMenu to make it transparent and keep both the menu and items non-opaque (this way there will be no opaque border of the menu like above).

EDIT:

Note that (unfortunately) it does not work when the popup menu exceeds the frame bounds, as @Durandal remarked.

Though you could try to make some calculations and change location of popup (when needed) to keep it always inside the frame.

Verdure answered 9/10, 2012 at 14:13 Comment(8)
@Karrikarrie no issue AlphaComposite is proper of ways (@Verdure +1)Manchu
Nope, try opening the popup so its partially outside the frame bounds. Epic. Or alternately setLightWeightPopupEnabled(false) on the popup.Karrikarrie
@Manchu It applies to both you and Rempelos. When the popup is realized in a heavyweight container (as it must when its not located within window bounds) the popup window will not be translucent.Karrikarrie
@Durandal: true, when partially outside the frame it's not transparent (noticed that after your comment actually). But still down vote was not necessary ;)Verdure
@Verdure Don't take it personally. I downvoted simply because it didn't work for me. Under Win Classic, it simply messed up the rendering for me when I tested it (item was transparent, parent panel was not, so it just became fainter).Karrikarrie
@Durandal, fair enough, no problem ;)Verdure
apart from not really working, I don't like it because it's tweaking the items - that's not really an option because it would require to manually create all items vs letting the menu/popup handle the creation given an Action.Jurisconsult
@Jurisconsult you can also do the opposite, extend JPopupMenu and make it non-opaque & transparent instead of the menu items which will be just non-opaque. It may be better that way (no opaque border of the menu like the image above)Verdure
R
1

See this excellent 2008 article Translucent and Shaped Swing Windows by Kirill Grouchnikov.

Based on examples given in the above article and code borrowed from the JGoodies project, you can install a custom popup Look & Feel using the following 2 classes:

  1. TranslucentPopup is the customized Popup, used by JPopupMenu:

    /**
     * Translucent Popup
     *
     * @author Kirill Grouchnikov [https://www.java.net/pub/au/275]
     */
    public class TranslucentPopup extends Popup {
        final JWindow popupWindow;
    
        TranslucentPopup(Component contents, int ownerX, int ownerY) {
            // create a new heavyweight window
            popupWindow = new JWindow();
            // mark the popup with partial opacity
            com.sun.awt.AWTUtilities.setWindowOpacity(popupWindow, 0.7f);
            // determine the popup location
            popupWindow.setLocation(ownerX, ownerY);
            // add the contents to the popup
            popupWindow.getContentPane().add(contents, BorderLayout.CENTER);
            contents.invalidate();
            JComponent parent = (JComponent) contents.getParent();
            // set a custom border
            parent.setBorder(BorderFactory.createRaisedSoftBevelBorder());
        }
    
        public void show() {
            popupWindow.setVisible(true);
            popupWindow.pack();
            // mark the window as non-opaque, so that the
            // border pixels take on the per-pixel opacity
            com.sun.awt.AWTUtilities.setWindowOpaque(popupWindow, false);
        }
    
        public void hide() {
            popupWindow.setVisible(false);
        }
    }
    
  2. TranslucentPopupFactory is requried to install the custom Look and Feel:

    /**
     * Translucent Popup Factory
     *
     * @author Kirill Grouchnikov [https://www.java.net/pub/au/275]
     * @author Karsten Lentzsch (JGoodies project)
     */
    public class TranslucentPopupFactory extends PopupFactory {
        /**
         * The PopupFactory used before this PopupFactory has been installed in
         * {@code #install}. Used to restored the original state in
         * {@code #uninstall}.
         */
        protected final PopupFactory storedFactory;
    
        protected TranslucentPopupFactory(PopupFactory originalFactory) {
            storedFactory = originalFactory;
        }
    
        public Popup getPopup(Component owner, Component contents, int x, int y) throws IllegalArgumentException {
            // A more complete implementation would cache and reuse popups
            return new TranslucentPopup(contents, x, y);
        }
    
        /**
          * Utility method to install the custom Popup Look and feel.
          * Call this method once during your application start-up.
          *
          * @author Karsten Lentzsch (JGoodies project)
          */
        public static void install() {
            PopupFactory factory = PopupFactory.getSharedInstance();
            if (factory instanceof TranslucentPopupFactory) {
                return;
            }
    
            PopupFactory.setSharedInstance(new TranslucentPopupFactory(factory));
        }
    
        /**
          * Utility method to uninstall the custom Popup Look and feel
          * @author Karsten Lentzsch (JGoodies project)
          */
        public static void uninstall() {
            PopupFactory factory = PopupFactory.getSharedInstance();
            if (!(factory instanceof TranslucentPopupFactory)) {
                return;
            }
    
            PopupFactory stored = ((TranslucentPopupFactory) factory).storedFactory;
            PopupFactory.setSharedInstance(stored);
        }
    }
    

The result is that all Popups (including JPopupMenu and perhaps also ToolTips) will now be Translucent, and optionally have a custom border: Translucent Context Menu shown on World Wind Context Menu example app

Rn answered 4/10, 2013 at 2:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.