0

I am attempting to create a custom popup menu.

Hard requirements are the following: 1) Speech bubble shape 2) Transparency 3) Menu is open until closed via a close button

I have managed to achieve all three of the above, but when I attempt to add a normal sub-menu, it just won't work. It renders as if it were a sub menu, but does not seem to register mouse interaction. I even tried adding an ActionListener to the menu directly, but that had no effect.

** EDIT ** Adding new sample based on feedback as lack of layout and color are not applicable to the problem I am trying to solve, original sample follows the new one

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.RoundRectangle2D;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;

public class CustomMenu extends JFrame
{
    public CustomMenu()
    {
        super();
        setUndecorated(true);
        setAlwaysOnTop(true);
        setOpacity(0.75f);
        setSize(100, 110);
        setLocation(800, 600);
        setShape(new RoundRectangle2D.Float(0, 0, 100, 110, 25, 25));
        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
        setBackground(Color.black);
        JButton closeButton = new JButton();
        closeButton.setSize(16, 16);
        closeButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                CustomMenu.this.dispose();
            }
        });
        closeButton.setLocation(75, 5);
        getContentPane().add(closeButton);

        JLabel label = new JLabel("Menu Title");
        label.setSize(90, 25);
        getContentPane().add(label);
        JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
        separator.setSize(90,5);
        getContentPane().add(separator);

        JMenu menu = new JMenu("Sub Menu");

        JMenuItem menuItem = new JMenuItem("Sub Menu Item One");
        menuItem.setSize(100, 25);
        menu.add(menuItem);

        menuItem = new JMenuItem("Sub Menu Item Two");
        menuItem.setSize(100, 25);
        menu.add(menuItem);

        menu.setSize(100, 25);
        getContentPane().add(menu);

        menuItem = new JMenuItem("Menu Item One");
        menuItem.setSize(100, 25);
        getContentPane().add(menuItem);

        menuItem = new JMenuItem("Menu Item Two");
        menuItem.setSize(100, 25);
        getContentPane().add(menuItem);

        menuItem = new JMenuItem("Menu Item Three");
        menuItem.setSize(100, 25);
        getContentPane().add(menuItem);
    }

    public static void main(String args[])
    {
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new CustomMenu().setVisible(true);
            }
        });
    }
}

Simplest self contained example I could come up with is below. Thanks in advance for any assistance.

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;

public class CustomMenu extends JFrame
{
    public CustomMenu()
    {
        super();
        setUndecorated(true);
        setAlwaysOnTop(true);
        setOpacity(0.75f);
        setSize(100, 110);
        setLocation(800, 600);
        setShape(new RoundRectangle2D.Float(0, 0, 100, 110, 25, 25));
        setLayout(null);
        setBackground(Color.black);
        JButton closeButton = new JButton();
        closeButton.setSize(16, 16);
        closeButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                CustomMenu.this.dispose();
            }
        });
        closeButton.setLocation(75, 5);
        add(closeButton);

        JLabel label = new JLabel("Menu Title");
        int y = 0;
        label.setLocation(10, y);
        label.setSize(90, 25);
        add(label);
        y += label.getHeight();
        JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
        separator.setSize(90,5);
        separator.setLocation(5, y);
        add(separator);
        y += separator.getHeight();

        JMenu menu = new JMenu("Sub Menu");

        JMenuItem menuItem = new JMenuItem("Sub Menu Item One");
        menuItem.setBackground(new Color(0,0,0,0));
        menuItem.setSize(100, 25);
        menuItem.setLocation(0,0);
        menu.add(menuItem);

        menuItem = new JMenuItem("Sub Menu Item Two");
        menuItem.setBackground(new Color(0,0,0,0));
        menuItem.setSize(100, 25);
        menuItem.setLocation(0,25);
        menu.add(menuItem);

        menu.setBackground(new Color(0,0,0,0));
        menu.setSize(100, 25);
        menu.setLocation(-1, y);
        add(menu);
        y += menu.getHeight();

        menuItem = new JMenuItem("Menu Item One");
        menuItem.setBackground(new Color(0,0,0,0));
        menuItem.setSize(100, 25);
        menuItem.setLocation(-1, y);
        add(menuItem);
        y += menuItem.getHeight();

        menuItem = new JMenuItem("Menu Item Two");
        menuItem.setBackground(new Color(0,0,0,0));
        menuItem.setSize(100, 25);
        menuItem.setLocation(-1, y);
        add(menuItem);
        y += menuItem.getHeight();

        menuItem = new JMenuItem("Menu Item Three");
        menuItem.setBackground(new Color(0,0,0,0));
        menuItem.setSize(100, 25);
        menuItem.setLocation(-1, y);
        add(menuItem);
    }

    public static void main(String args[])
    {
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new CustomMenu().setVisible(true);
            }
        });
    }
}
Gno Whey
  • 1
  • 1
  • 1

1 Answers1

1

Problems...

  1. Null layout
  2. Negative layout position
  3. Alpha based background color

Swing doesn't like negative positions for its components

Swing can't render alpha based background colors, it only knows how to render opaque and fully transparent components. Using an alpha based color will prevent swing from updating the area beneath the componet, which leads to weird paint artefacts and other issues

Solutions...

  1. Use a appropriate layout manager
  2. See point 1.
  3. Use setOpaque. If you need a translucent background, you'll need to create a custom component and override its paintComponent method and paint the background yourself

It should be noted, you can actually control the popup window that sub menus use, these a created deep down in the bowels of the api (I think maybe even via a static method, but it's been a while since I dug that deep)

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I really hate trying to find a layout manager that works. Box layout is close, but won't allow me to position the close button over the first element. This frame will not be resized, so I am getting no benefit from a layout manager. – Gno Whey Apr 25 '15 at 01:02
  • As for the translucent background, what I need is for all of the components to be translucent. I am getting that with the JFrame, I just don't want the background color to be present at all, hence the fully transparent color. – Gno Whey Apr 25 '15 at 01:05
  • What isn't working at all is the sub-menu, any thoughts on why that might be? – Gno Whey Apr 25 '15 at 01:06
  • 1- Use a combination of layout mansgers. Maybe BorderLayout and GridBagLayout. I think there's a "menu layout" buried somewhere within the look and feel code; 2- as I said, if you want transparency, you'll need to set the components fully transparent and override their paintComponet methods and fill the background yourself; As for the submenus, I'm afraid you're out of luck as the "window" which is used to find play them is generated by the system and is buried deep within the core code. About the only thing I can think of is to generate all yourself from scratch, including the menu items – MadProgrammer Apr 25 '15 at 01:27