macOS

This guide describes how to adapt your Java Swing application to macOS.

Global settings

Screen menu bar

While Windows or Linux applications have there menu bars within the main window, macOS applications show the menu bar on top of the screen.

If your main window is a JFrame and has a menu bar (set with JFrame.setJMenuBar(JMenuBar)), then it looks like this on macOS:

macOS screen menu bar disabled

To move the menu bar out of the main window to the top of the screen, add following to the main method of your application (before any UI code):

System.setProperty( "apple.laf.useScreenMenuBar", "true" );

Then it looks like this:

macOS screen menu bar enabled

To integrate you application with the application menu in the screen menu bar, see application menu integration.

Application name

The application name shown in the screen menu bar (between the apple and "File") is the name of the main class of your application. This maybe is not equal to your application name. You can fix it with:

System.setProperty( "apple.awt.application.name", "FlatLaf Demo" );

macOS application name

Appearance of window title bars

macOS supports light and dark appearances.

If you enable dark appearance in macOS "System Preferences" and use a dark FlatLaf theme, then the window title bar of the application still has a light color:

macOS application appearance dark

To fix this use following:

System.setProperty( "apple.awt.application.appearance", "system" );

Then the window title bars automatically change color when system appearance changes:

macOS application appearance system

Notes:

  • available since Java 14; backported to Java 13.0.4, 11.0.8 and 8u322
  • must be set on main thread and before AWT/Swing is initialized; setting it on AWT thread does not work

Window settings

Transparent title bar

A nice feature that allows you to give your application a more modern (and flat) look is transparent title bar. (available since Java 12; backported to Java 11.0.8 and 8u292; see JDK-8211301)

To enable transparent title bar for a window, set following client property on the root pane of the window:

if( SystemInfo.isMacFullWindowContentSupported )
    frame.getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );

macOS transparent title bar

Dark appearance (with apple.awt.application.appearance=system):

macOS transparent title bar

Unfortunately this behaves different for older Java versions:

  • for Java 8 - 15, the background color of the AWT/String window is used as background for the window title bar, but system color is used for the title text, which may result in unreadable title (e.g. when using dark system appearance and light FlatLaf theme; white text and light gray background)
  • for Java 16 and later, the macOS system colors are always used for window title bar background and title text

Full window content

Full window content is another nice feature that allows you to extend the content into the window title bar. This means that the root pane of the Swing window is extended into the macOS window title bar. macOS still shows the red/orange/green buttons and the window title. It is possible to drag the window by click-and-drag at the top area of the window. (available since Java 12; backported to Java 11.0.8 and 8u292; see JDK-8211301)

To enable full window content for a window, set following client properties on the root pane of the window:

if( SystemInfo.isMacFullWindowContentSupported ) {
    frame.getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
    frame.getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
}

macOS full window content

macOS full window content

Note that the red/orange/green buttons and the window title overlap the Swing components (e.g. the toolbar in above screenshots). You have to modify your application and add some empty space to avoid the overlapping. In case of a toolbar, adding a 70px wide horizontal strut works very well:

if( SystemInfo.isMacFullWindowContentSupported )
    toolBar.add( Box.createHorizontalStrut( 70 ), 0 );

macOS full window content

Note that the window title is painted using the system appearance. So if you use light system appearance but a dark FlatLaf theme, it looks like this:

macOS full window content

Dark system appearance and light FlatLaf theme:

macOS full window content

Hide window title

To avoid above problem or to avoid that the window title operlaps Swing components, you can hide the window title. (available since Java 17; see JDK-8265005)

frame.getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );

For older Java versions use:

frame.setTitle( null );

macOS hide window title

macOS hide window title

Full screen (pre Java 11)

When running your application on Java 8 - 10, the green button in the window title bar maximizes (zooms) the window. When you place the mouse over the green button, following menu is shown:

macOS zoomable

If you like to support full screen mode, as many native macOS applications do, set following client property on the root pane of the window:

frame.getRootPane().putClientProperty( "apple.awt.fullscreenable", true );

Then clicking the green button enters full screen mode. You can still maximize the window by holding down the Alt key while pressing the green button.

macOS full screen

Code Examples

Here are some full examples that you can copy to your application.

For all windows:

public static void main( String[] args ) {
    // macOS  (see https://www.formdev.com/flatlaf/macos/)
    if( SystemInfo.isMacOS ) {
        // enable screen menu bar
        // (moves menu bar from JFrame window to top of screen)
        System.setProperty( "apple.laf.useScreenMenuBar", "true" );

        // application name used in screen menu bar
        // (in first menu after the "apple" menu)
        System.setProperty( "apple.awt.application.name", "My Application" );

        // appearance of window title bars
        // possible values:
        //   - "system": use current macOS appearance (light or dark)
        //   - "NSAppearanceNameAqua": use light appearance
        //   - "NSAppearanceNameDarkAqua": use dark appearance
        // (must be set on main thread and before AWT/Swing is initialized;
        //  setting it on AWT thread does not work)
        System.setProperty( "apple.awt.application.appearance", "system" );
    }

    SwingUtilities.invokeLater( () -> {
        FlatLightLaf.setup();

        // create and show UI
    } );
}

For main frame:

// macOS  (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) {
    // hide menu items that are in macOS application menu
    aboutMenuItem.setVisible( false );
    preferencesMenuItem.setVisible( false );
    exitMenuItem.setVisible( false );

    // do not use HTML text in menu items because this is not supported in macOS screen menu
    htmlMenuItem.setText( "some text" );

    if( SystemInfo.isMacFullWindowContentSupported ) {
        // expand window content into window title bar and make title bar transparent
        frame.getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
        frame.getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );

        // hide window title
        if( SystemInfo.isJava_17_orLater )
            frame.getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
        else
            frame.setTitle( null );

        // add gap to left side of toolbar
        toolBar.add( Box.createHorizontalStrut( 70 ), 0 );
    }

    // enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
    if( !SystemInfo.isJava_11_orLater )
        frame.getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
}

Application menu integration

If the screen menu bar is enabled, macOS adds an application menu to the menu bar.

macOS application menu

By default, About AppName shows a default about dialog, Preferences is hidden and Quit AppName always quits the application.

Use class java.awt.Desktop to integrate you application with those three menu items (requires Java 9 or later):

Desktop desktop = Desktop.getDesktop();
if( desktop.isSupported( Desktop.Action.APP_ABOUT ) ) {
    desktop.setAboutHandler( e -> {
        // show about dialog
    } );
}
if( desktop.isSupported( Desktop.Action.APP_PREFERENCES ) ) {
    desktop.setPreferencesHandler( e -> {
        // show preferences dialog
    } );
}
if( desktop.isSupported( Desktop.Action.APP_QUIT_HANDLER ) ) {
    desktop.setQuitHandler( (e, response) -> {
        boolean canQuit = // TODO
        if( canQuit )
            response.performQuit();
        else
            response.cancelQuit();
    } );
}

If your application uses Java 8, then use class com.formdev.flatlaf.extras.FlatDesktop from flatlaf-extras library:

FlatDesktop.setAboutHandler( () -> {
    // show about dialog
} );
FlatDesktop.setPreferencesHandler( () -> {
    // show preferences dialog
} );
FlatDesktop.setQuitHandler( response -> {
    boolean canQuit = // TODO
    if( canQuit )
        response.performQuit();
    else
        response.cancelQuit();
} );

Don't forget to hide the About, Preferences and Exit menu items from the menu.

if( SystemInfo.isMacOS ) {
    // hide menu items that are in macOS application menu
    aboutMenuItem.setVisible( false );
    preferencesMenuItem.setVisible( false );
    exitMenuItem.setVisible( false );
}