Java Swing - Add leniency when selecting items in submenus

强颜欢笑 提交于 2019-12-05 06:29:01

Call setDelay(delay) on your menu variable, where the delay parameter is the amount of milliseconds to wait for the menu to show, as an int.

This following line of code will set the delay to 1 second, so the user has to mouseover the menu item "Menu n" 1 second, before the submenu is displayed: menu.setDelay(1000);

Here's a snippet of the edited code:

for (int i = 0; i < 5; i++)
{
    JMenu menu = new JMenu("Menu " + i);
    pMenu.add(menu);
    for (int j = 0; j < 10; j++)
    {
        menu.add(new JMenuItem("Item " + j));
    }
    menu.setDelay(1000);
}

I came up with a very hacky solution.

I made a UI class that extends BasicMenuUI. I override the createMouseInputListener method to return a custom MouseInputListener instead of the private handler object inside BasicMenuUI.

I then got the code for the MouseInputListener implementation in handler from GrepCode[1], and copied it into my custom listener. I made one change, putting a timer in mouseEntered. My final code for mouseEntered looks like this:

public void mouseEntered(MouseEvent e) {
        timer.schedule(new TimerTask() {

            @Override
            public void run() {
                if (menuItem.isShowing())
                {
                    Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
                    Point menuLoc = menuItem.getLocationOnScreen();
                    if (mouseLoc.x >= menuLoc.x && mouseLoc.x <= menuLoc.x + menuItem.getWidth() &&
                            mouseLoc.y >= menuLoc.y && mouseLoc.y <= menuLoc.y + menuItem.getHeight())
                    {
                        originalMouseEnteredStuff();
                    }
                }
            }
        }, 100);
    }

Before calling the the original code that was in mouseEntered, I check to make sure the mouse is still within this menu's area. I don't want all the menus my mouse brushes over to pop up after 100 ms.

Please let me know if anyone has discovered a better solution for this.

[1] http://www.grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/7-b147/javax/swing/plaf/basic/BasicMenuUI.java/?v=source

Thank you very much, you saved my day! The solution works as expected but I recommend using the Swing timer to ensure the code is executed by the EDT.

Additionally you should temporary set the menus delay to zero before calling the original stuff. Otherwise the user has to wait twice the delay time.

@Override
public void mouseEntered(MouseEvent e) {
    if (menu.isTopLevelMenu() || menu.getDelay() == 0) {
        originalMouseEnteredStuff(e);
    } else {
        final javax.swing.Timer timer = new javax.swing.Timer(menu.getDelay(), new DelayedMouseEnteredAction(e));
        timer.setRepeats(false);
        timer.start();
    }
}
class DelayedMouseEnteredAction implements ActionListener
{
    private final MouseEvent mouseEnteredEvent;

    private DelayedMouseEnteredAction(MouseEvent mouseEnteredEvent) {
        this.mouseEnteredEvent = mouseEnteredEvent;
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        if (menu.isShowing()) {
            final Point mouseLocationOnScreen = MouseInfo.getPointerInfo().getLocation();
            final Rectangle menuBoundsOnScreen = new Rectangle(menu.getLocationOnScreen(), menu.getSize());
            if (menuBoundsOnScreen.contains(mouseLocationOnScreen)) {
                /*
                 * forward the mouse event only if the mouse cursor is yet
                 * located in the menus area.
                 */
                int menuDelay = menu.getDelay();
                try {
                    /*
                     * Temporary remove the delay. Otherwise the delegate would wait the
                     * delay a second time e.g. before highlighting the menu item.
                     */
                    menu.setDelay(0);
                    originalMouseEnteredStuff(mouseEnteredEvent);
                } finally {
                    // reset the delay
                    menu.setDelay(menuDelay);
                }
            }
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!