Adding a scrollable menu to a submenu

738 Views Asked by At

I'm trying to adapt to submenus the code found at https://coderanch.com/t/343946/java/Scrolling-JMenu in order to have a scrollbar rather than buttons like suggested in the answer of Java: Creating a scrolling submenu. However I am having a some troubles for getting a "standard" behaviour:

  1. The parent menu should not disappear when the mouse is focused/hovering on the scrollable menu (see Font menu).
  2. The scrollable menu should disappear when the mouse is not focused/hovering on it or on its parent submenu (see Font menu).
  3. The scrollable menu should not disappear even if the text of the submenu is long (see Help menu).

Thanks in advance for your help. Here's my code:

import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;

public class ScrollingMenu extends JFrame {

    private final JPopupMenu fontNameMenu = new JPopupMenu();
    private final JMenu[] fontMenuArray = new JMenu[] {new JMenu("Font Name"), new JMenu("Font Style")};
    private JMenu menu = new JMenu("Font");
    private final JMenu[] helpMenuArray = new JMenu[] {new JMenu("Very very very very very very very long Font Name Menu")};
    private JMenu hmenu = new JMenu("Help");
    
    public static void main(String[] args) {
        JFrame scrollingMenu = new ScrollingMenu();
        scrollingMenu.setVisible(true);
    }

    public ScrollingMenu() {
        
        // Creating a long array
        String[] systemFontArray = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
        JCheckBoxMenuItem[] fontNameArray = new JCheckBoxMenuItem[systemFontArray.length];
        
        // Creating the scrollable menu
        GridBagLayout fontNameLayout=new GridBagLayout();
        JPanel fontNamePanel = new JPanel();
        fontNamePanel.setLayout(fontNameLayout);
        for (int i=0;i<systemFontArray.length;i++) {
            fontNameArray[i] = new JCheckBoxMenuItem(systemFontArray[i]);
            fontNameLayout.setConstraints(fontNameArray[i],new GridBagConstraints(0,i,1,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0));
            fontNamePanel.add(fontNameArray[i]);
        }
        JScrollPane fontScrollPane = new JScrollPane(fontNamePanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        this.fontNameMenu.add(fontScrollPane);

        // Adding the scrollable menu to a submenu
        addML(ScrollingMenu.this.fontMenuArray[0], ScrollingMenu.this.fontNameMenu, new Dimension(200, 200));
        
        // Creating the Font Style submenu (for underlining problem 2)
        this.fontMenuArray[1].add(new JCheckBoxMenuItem("Bold"));
        this.fontMenuArray[1].add(new JCheckBoxMenuItem("Italic"));
        this.fontMenuArray[1].add(new JCheckBoxMenuItem("Plain"));
        
        // Adding the submenus to the Font menu
        for (int i=0;i<this.fontMenuArray.length;i++) {
            this.menu.add(this.fontMenuArray[i]);
        }
        
        // Adding the scrollable menu to a long text submenu (problem 3)
        addML(ScrollingMenu.this.helpMenuArray[0], ScrollingMenu.this.fontNameMenu, new Dimension(200, 200));
        
        // Adding the submenu to the Help menu
        for (int i=0;i<this.helpMenuArray.length;i++) {
            this.hmenu.add(this.helpMenuArray[i]);
        }
        
        JMenuBar menuBar = new JMenuBar();
        menuBar.add(this.menu);
        menuBar.add(this.hmenu);
        this.setJMenuBar(menuBar);
        this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        this.pack();
        this.setLocationRelativeTo(null);
    }

    private static void addML (JMenu subMenu, JPopupMenu popupMenu, Dimension dimension) {
        popupMenu.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent focusEvent) {
                if(popupMenu.isVisible() && !subMenu.isSelected())
                    popupMenu.setVisible(false);
            }
        });
        popupMenu.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseExited(MouseEvent mouseEvent) {
                if(!subMenu.isSelected())
                    popupMenu.setVisible(false);
            }
        });
        popupMenu.setInvoker(subMenu);
        popupMenu.setPreferredSize(dimension);
        subMenu.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent mouseEvent) {
                if(!popupMenu.isVisible())
                    displayFontNameMenu(subMenu, popupMenu);
                else
                    popupMenu.setVisible(false);
            }
           @Override
            public void mouseEntered(MouseEvent mouseEvent) {
                if(!popupMenu.isVisible() && subMenu.isSelected())
                    displayFontNameMenu(subMenu, popupMenu);
            }
           @Override
            public void mouseExited(MouseEvent mouseEvent) {
                if(popupMenu.isVisible() && !popupMenu.contains(mouseEvent.getPoint())) {
                    popupMenu.setVisible(false);
                }
            }
        });
    }

    private static void displayFontNameMenu(JMenu subMenu, JPopupMenu popupMenu) {
        Rectangle rectangle = subMenu.getBounds();
        Point point = new Point(rectangle.x  +rectangle.width, rectangle.y);
        SwingUtilities.convertPointToScreen(point, subMenu.getParent());
        popupMenu.setLocation(point.x, point.y);
        popupMenu.setVisible(true);
    }

}
0

There are 0 best solutions below