3

This question is a follow-up to this question.

I have a JMenuBar whose behavior is like the menu bars in Firefox and iTunes. That is, the menu bar is initially hidden, but when you press Alt, the menu bar appears.

The answer to the other question solved the problem of achieving that functionality, but it brought about another issue: The JMenuItem accelerators are not working when the JMenuBar is not visible. In other words, you must press Alt before CTRL+F (the installed accelerator) will work.

This is not supposed to be the case, though, because the setAccelerator() method states the following:

public void setAccelerator(KeyStroke keyStroke)

Sets the key combination which invokes the menu item's action listeners without navigating the menu hierarchy. It is the UI's responsibility to install the correct action. Note that when the keyboard accelerator is typed, it will work whether or not the menu is currently displayed.

So, I'm wondering if this is another Java bug?

SSCCE (to get the menu to appear, you press Alt, and the installed accelerator is CTRL+F for "Find" which brings up a dummy JOptionPane for input):

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class MenuBarTest extends JFrame {

    public MenuBarTest() {
        JMenu jMenu1 = new JMenu();
        JMenu jMenu2 = new JMenu();
        final JMenuBar jMenuBar1 = new JMenuBar();
        JMenuItem jMenuItem1 = new JMenuItem();
        JMenuItem jMenuItem2 = new JMenuItem();

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        jMenu1.setText("File");
        jMenuItem1.setText("jMenuItem1");
        jMenu1.add(jMenuItem1);
        jMenuBar1.add(jMenu1);
        jMenu2.setText("Edit");
        jMenuItem2.setText("Find");
        jMenu2.add(jMenuItem2);
        jMenuBar1.add(jMenu2);
        setJMenuBar(jMenuBar1);

        jMenuBar1.setVisible(false);
        ChangeListener listener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                MenuElement[] elements = MenuSelectionManager.defaultManager().getSelectedPath();
                jMenuBar1.setVisible(elements.length > 0 && elements[0] == jMenuBar1);
            }
        };
        MenuSelectionManager.defaultManager().addChangeListener(listener);

        jMenuItem2.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F, java.awt.event.InputEvent.CTRL_MASK));
        jMenuItem2.setText("Find");
        jMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                String what = JOptionPane.showInputDialog(MenuBarTest.this, "Search for what?");
                System.out.println(what);
            }
        });

        pack();
        setSize(500,500);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String args[]) {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MenuBarTest();
            }
        });
    }
}
Community
  • 1
  • 1
ryvantage
  • 13,064
  • 15
  • 63
  • 112
  • my take is that it's a documentation issue: the "displayed" here means addresses the normal state that a menu/item under a menuBar is hidden (because we see only the menubar). Nevertheless, it is _showing_ (because it's visible and all of its ancestors are showing), thus allowing bindings to work. – kleopatra Aug 09 '13 at 21:10

1 Answers1

4

Read your emphasis carefully

Note that when the keyboard accelerator is typed, it will work whether or not the menu is currently displayed.

This talks about the menu not its parent. Meaning that the menu might be currently not displayed. Nevertheless, the real (probably not overly well documented) desisive property is that it must be showing. Had updated my answer to your previous question.

kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • Wow. Like the last question I would've never thought of that on my own. This community is filled with truly gifted developers! – ryvantage Aug 09 '13 at 21:09
  • Oh how I love Java... So, I extended `JMenuBar` and (ok, don't throw tomatoes at me) tried to import the new class into Netbeans (yes, the GUI builder), and I ran into this fun little detail: not all of `JMenuBar`'s methods have been completed. Someone complained about this in (get this) 1999: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4087846 and it STILL has not been fixed. A blog article, aptly titled, "wtf" has been created in this bug's honor.. in, oh, 2006. http://tsareto.blogspot.com/2006/05/wtf.html. So, don't call `JMenuBar.getHelpMenu()`, because Java hasn't gotten to it yet. – ryvantage Aug 10 '13 at 02:44
  • SSCCE: `public static void main(String[] args) { JMenuBar bar = new JMenuBar(); bar.getHelpMenu(); }` – ryvantage Aug 10 '13 at 02:45
  • `@Override public JMenu getHelpMenu() { return null; }` did the trick lol – ryvantage Aug 10 '13 at 02:48