3

I have a JFrame with a JMenuBar that I'm developing and testing on a Mac system. On this JFrame, I have programmed one of the JMenus to be disabled. However, when I change focus from my Java application to some other program on my computer and then come back to this JFrame, strange things are happening. I have observed that all of the menus become disabled. I have also observed that all of the menus become enabled. Can anyone tell me what's going on here?

Here's a piece of code that will reproduce the error (at least it does on my machine):

public class MenuProblemExample {

    public static void main(String[] args) {
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        JFrame frame = new JFrame();
        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("File");
        fileMenu.add(new JMenuItem("open"));
        menuBar.add(fileMenu);
        JMenu editMenu = new JMenu("Edit");
        editMenu.add(new JMenuItem("select all"));
        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        frame.setJMenuBar(menuBar);
        fileMenu.setEnabled(false);
        frame.setVisible(true);        
    }
}

When I run this, the enabled property isn't stable under the action of switching focus to another window and then back again.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 1
    Can you post an SSCCE or a screenshot? – Branislav Lazic Dec 07 '12 at 00:21
  • Everything work fine for me. I'm not Mac OS user, but I bet that something is wrong in this line of code: `System.setProperty("apple.laf.useScreenMenuBar", "true");` Why are you setting that property at all? – Branislav Lazic Dec 07 '12 at 00:48
  • It causes the frame's menubar to display at the top of the screen the way native apps do in the Mac OSX operating system. –  Dec 07 '12 at 00:53
  • And `UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());` stands for? :) – Branislav Lazic Dec 07 '12 at 00:55
  • I don't understand your question. That code is not in my example. –  Dec 07 '12 at 00:55
  • Put that in your `main` method. Also suround it with `try/cath` block, and tell me how your menu looks like. P.S. Remove that `System.setProperty("apple.laf.useScreenMenuBar", "true");` line. – Branislav Lazic Dec 07 '12 at 00:57
  • I just did that and I commented out the System.setProperty("apple.laf.useScreenMenuBar", "true") It has the effect that the menubar is displayed within the frame (not at the top of the screen). In this situation, there is no problem with the JMenu enabled property. It's working correctly. But I want the menu at the top of the screen. –  Dec 07 '12 at 01:00
  • At the top of the screen? What do you mean by that? – Branislav Lazic Dec 07 '12 at 01:01
  • For example: http://monodevelop.com/@api/deki/files/256/=MacMainMenu.png –  Dec 07 '12 at 01:04
  • Woa...I'm out. To be honnest, currently, I have no idea how to solve that. – Branislav Lazic Dec 07 '12 at 01:07
  • I suspect it's a bug, but I figure anyone who has developed a Java application on a Mac has run into it and might point me in the right direction to fix it. –  Dec 07 '12 at 01:08
  • I also suspect part of the problem is that I'm calling setEnabled(false) on JMenu elements rather than the JMenuItem elements they contain. I did not reproduce the problem when I changed the code to disable individual JMenuItems. –  Dec 07 '12 at 01:17
  • Swing GUI objects should be constructed and manipulated _only_ on the [event dispatch thread](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod Dec 07 '12 at 01:42
  • @trashgod: could you explain why or link to someplace where your comment is justified? Also, I don't have the foggiest idea how to accomplish what you suggest. I'd appreciate pointers or a link. –  Dec 07 '12 at 02:16
  • I found this article http://www.javaworld.com/javaworld/jw-08-2007/jw-08-swingthreading.html?page=5 which seems relevant to trashgod's comment. I'm still learning and would welcome any more comments or links to good reading material. –  Dec 07 '12 at 02:32

1 Answers1

3

Swing GUI objects should be constructed and manipulated only on the event dispatch thread.

By design, Mac applications using the screen menubar disable menus in the background. It is up to your application to enable menus appropriately when your application comes to the foreground, using e.g. a WindowListener.

Addendum: For testing, this revised example adds a toggle button that tracks the state of the File > Open menu item.

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToggleButton;

/**
 * @see http://stackoverflow.com/a/13756527/230513
 */
public class MenuProblemExample {

    public static void main(String[] args) {
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                JMenuBar menuBar = new JMenuBar();
                JMenu fileMenu = new JMenu("File");
                final JMenuItem openItem = new JMenuItem("open");
                openItem.setEnabled(false);
                fileMenu.add(openItem);
                menuBar.add(fileMenu);
                JMenu editMenu = new JMenu("Edit");
                editMenu.add(new JMenuItem("select all"));
                menuBar.add(fileMenu);
                menuBar.add(editMenu);
                frame.setJMenuBar(menuBar);
                frame.add(new JToggleButton(new AbstractAction("Toggle") {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        JToggleButton b = (JToggleButton) e.getSource();
                        openItem.setEnabled(b.isSelected());
                    }
                }));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • `org.jhotdraw.samples.draw.Main`, cited [here](http://stackoverflow.com/q/13312223/230513), is an example. – trashgod Dec 07 '12 at 03:44
  • Well, I can certainly use a WindowListener, but I'm curious that you say this behavior is "by design". Of course that menu has to vanish when my application is not in focus, but I don't see how it is ever desirable that the properties of my menu items should change just from moving focus to another window and back. Especially considering I have seen this vary unpredictably from all menu items suddenly enabled to all menu items suddenly disabled, I think this is a bug. –  Dec 07 '12 at 08:45
  • I can't rule out a bug, but Apple's [*Human Interface Guidelines*](https://developer.apple.com/library/mac/#documentation/userexperience/Conceptual/AppleHIGuidelines/Intro/Intro.html) specifies managing individual item's _enabled_ state. If you _don't_ work on the EDT, you'll experience unpredictable behavior. Once you're on the EDT, as shown above, it's easier to stay on. – trashgod Dec 07 '12 at 11:37
  • Thanks for pointing out that issue about working on the event dispatch thread. It was among the things I didn't know I didn't know. –  Dec 07 '12 at 19:03
  • @AdamCross: It's foundational, but easily overlooked. I think you're right about items v. menus. I've updated the example for reference. – trashgod Dec 07 '12 at 23:02