Swing applications and Mac OS X menu bar
July 13th, 2008 | 16 Comments »Every once in a while i get questions on using the Mac OS X menu bar for Swing applications running under Substance look-and-feel. This refers to the apple.laf.useScreenMenuBar VM flag that is respected by the native Aqua look-and-feel (and its third-party Quaqua extension). Up until this week the only advice that i could give was to use AWT menus (thanks to Quaqua’s author Werner Randelshofer for this). However, it is not the optimal solution for cross-platform Swing applications that wish to use Swing menus on non-Mac platform.
As i was thinking about this problem after being recently contacted by Sergiy Michka, i thought about an alternative solution which was later reviewed by Swing lead for Apple VM Mike Swingler. The solution is not specific to Substance and should work under other core and third-party look-and-feels that allow mixing menu UI delegates from other look-and-feels. Here is what you can do in your Swing application to have your menus appear on the global menu bar (in addition to setting the above VM flag):
- Check that you’re running under Apple VM. Use
System.getProperty("os.name")and check that the value starts with “Mac”. - Set Aqua (or the future default Apple VM platform look-and-feel) with
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()). - Query and store the following
UIManagerentries: “MenuBarUI”, “MenuUI”, “MenuItemUI”, “CheckBoxMenuItemUI”, “RadioButtonMenuItemUI”, “PopupMenuUI”. - Set your look-and-feel of preference with
UIManager.setLookAndFeelcall. - Before creating your first window, restore the
UIManagerentries for the keys specified in the third step.
Now the menus on the fronted frame will go into the global menu bar. It is not recommended to rely on the current class names for the Aqua UI delegates, since the package that hosts these classes is different for different versions of Apple VM. This is mentioned in the release notes for Apple VM 1.6.0 on Mac OS X 10.5 Leopard (radar #4907470).
Related posts:
- New Graphite Aqua skin for Swing applications In addition to the usual bug fixes and getting the internal implementation ready for the...
- New Creme Coffee skin for Swing applications Just before the feature freeze of Substance 4.1, i added a new Coffee Cream skin...
- New Twilight skin for Swing applications In addition to bug fixes and performance improvements in Substance 5.2 (code-named Quebec), there are...
- New Dust Coffee skin for Swing applications Substance 5.2 (code-named Quebec) is going to have a few new skins. About a week...
Hmmm, this is interesting, I wonder if the process could be extracted out into laf-widget, so that all look and feels could benefit from it.
Luke,
This would be more for the applications and not for the look-and-feels (which is what laf-widget is for).
Thanks
Kirill
Hi Kirill,
Thanks for this. I would really appreciate if you could give us a small code to illustrate this [Only JFrame with menu will be excellent]
Thanks Kirill
abdulla – i do not have access to Mac environment, so i cannot give you code that i have personally tested. Which step gives you the trouble?
Thanks
Kirill
I don’t understand this:
Query and store the following UIManager entries: “MenuBarUI”, “MenuUI”, “MenuItemUI”, “CheckBoxMenuItemUI”, “RadioButtonMenuItemUI”, “PopupMenuUI”.
thanks Kirill
Call UIManager.get(“MenuBarUI”). This will give you an Object which is a fully-qualified class name of the UI delegate for menu bar components. Then, after setting the LAF of your choice, call UIManager.put(“MenuBarUI”, previouslyStoredValue). Do this for all the UI delegate keys mentioned in the third step.
Thanks for the comment Kirill. I will give it a try
It works on Tiger/PPC.
Hi Kirill,
I searched the internet for a way to programmatically find all the laf in a class path, but i failed.
I must admit beeing a swing newbie
Regards
Thanks for this! I just ran into this problem today when trying to use a a non-aqua look&feel on the Mac, and still wanting to use the Mac OS menu bar. This works for me on Java 5 on an Intel machine with OS X 10.4.11.
Raphael,
I don’t think that you can do this. In general, a class can be constructed dynamically when the class loader is requested to return it. It’s like trying to search the class path for all classes that implement a certain interface.
If your class loader is URLClassLoader, you can scan all the urls yourself, inspect the class and see if it has any derivatives of LookAndFeel. This will not only be expensive, but it will also miss the dynamically created classes.
And UIManager.getInstalledLookAndFeels only returns those explicitly installed or registered with the runtime.
If you have other general questions on Swing, please post them on the Swing forums on java.net and java.sun.com
Thanks
Kirill
Thanks kirill
All I needed was your first suggestion, System.setProperty(“apple.laf.useScreenMenuBar”, “true”);
I tried the UIManager.get and set methods first, but it did nothing. I added the first suggestion and commented out the UIManager.get and set methods and it worked fine. Thanks.
Mario,
If you’re running under Aqua (which is the default Apple VM look-and-feel), then this indeed is the only thing you need. This article is about other (core / third-party) look-and-feels
Thanks
Kirill
Thanks for the hint. Following your instructions I get this exception after displaying a JFrame with a JMenue. Any ideas?
Thanks
Steffen
Exception in thread “AWT-EventQueue-0″ java.lang.NullPointerException
at apple.laf.CUIAquaMenuPainter.paintMenuBarBackground(CUIAquaMenuPainter.java:138)
at apple.laf.CUIAquaMenuBar.paint(CUIAquaMenuBar.java:51)
at javax.swing.plaf.ComponentUI.update(ComponentUI.java:154)
at javax.swing.JComponent.paintComponent(JComponent.java:743)
at javax.swing.JComponent.paint(JComponent.java:1006)
at javax.swing.JComponent.paintChildren(JComponent.java:843)
at javax.swing.JComponent.paint(JComponent.java:1015)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:559)
at javax.swing.JComponent.paintChildren(JComponent.java:843)
at javax.swing.JComponent.paint(JComponent.java:1015)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:34)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
at java.awt.Container.paint(Container.java:1722)
at sun.awt.RepaintArea.paintComponent(RepaintArea.java:276)
at sun.awt.RepaintArea.paint(RepaintArea.java:241)
at apple.awt.ComponentModel.handleEvent(ComponentModel.java:268)
at apple.awt.CWindow.handleEvent(CWindow.java:255)
at java.awt.Component.dispatchEventImpl(Component.java:4144)
at java.awt.Container.dispatchEventImpl(Container.java:2068)
at java.awt.Window.dispatchEventImpl(Window.java:1791)
at java.awt.Component.dispatchEvent(Component.java:3903)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
Stefen,
What is your main look-and-feel? Do you have this exception when you’re running your app completely under Aqua? Please note that since i don’t have any access to Mac environment, if you don’t find the source of this problem, i’ll have to defer this question to the community.
Thanks
Kirill