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):

  1. Check that you’re running under Apple VM. Use System.getProperty("os.name") and check that the value starts with “Mac”.
  2. Set Aqua (or the future default Apple VM platform look-and-feel) with UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()).
  3. Query and store the following UIManager entries: “MenuBarUI”, “MenuUI”, “MenuItemUI”, “CheckBoxMenuItemUI”, “RadioButtonMenuItemUI”, “PopupMenuUI”.
  4. Set your look-and-feel of preference with UIManager.setLookAndFeel call.
  5. Before creating your first window, restore the UIManager entries 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:

  1. New Graphite Aqua skin for Swing applications In addition to the usual bug fixes and getting the internal implementation ready for the...
  2. New Creme Coffee skin for Swing applications Just before the feature freeze of Substance 4.1, i added a new Coffee Cream skin...
  3. New Twilight skin for Swing applications In addition to bug fixes and performance improvements in Substance 5.2 (code-named Quebec), there are...
  4. New Dust Coffee skin for Swing applications Substance 5.2 (code-named Quebec) is going to have a few new skins. About a week...


16 Comments on “Swing applications and Mac OS X menu bar”

  1. 1 Luke Sleeman said at 6:24 pm on July 13th, 2008:

    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.

  2. 2 Kirill Grouchnikov said at 7:20 pm on July 13th, 2008:

    Luke,

    This would be more for the applications and not for the look-and-feels (which is what laf-widget is for).

    Thanks
    Kirill

  3. 3 abdulla said at 7:35 pm on July 14th, 2008:

    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

  4. 4 Kirill Grouchnikov said at 9:08 pm on July 14th, 2008:

    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

  5. 5 abdulla said at 9:39 pm on July 14th, 2008:

    I don’t understand this:

    Query and store the following UIManager entries: “MenuBarUI”, “MenuUI”, “MenuItemUI”, “CheckBoxMenuItemUI”, “RadioButtonMenuItemUI”, “PopupMenuUI”.

    thanks Kirill

  6. 6 Kirill Grouchnikov said at 9:48 pm on July 14th, 2008:

    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.

  7. 7 abdulla said at 9:58 pm on July 14th, 2008:

    Thanks for the comment Kirill. I will give it a try

  8. 8 Jan Erik Paulsen said at 5:10 pm on July 17th, 2008:

    It works on Tiger/PPC.

  9. 9 Raphaël said at 1:01 am on July 18th, 2008:

    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

  10. 10 Rob Ross said at 2:02 am on July 18th, 2008:

    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.

  11. 11 Kirill Grouchnikov said at 8:03 am on July 18th, 2008:

    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

  12. 12 Raphaël said at 4:04 pm on July 19th, 2008:

    Thanks kirill

  13. 13 Mario Brunetti said at 4:53 pm on July 24th, 2008:

    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.

  14. 14 Kirill Grouchnikov said at 7:30 pm on July 24th, 2008:

    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

  15. 15 Stefen Greiffenberg said at 3:41 am on August 4th, 2008:

    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)

  16. 16 Kirill Grouchnikov said at 8:07 am on August 4th, 2008:

    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