macOS window button style: support NSWindowToolbarStyleUnified (availaible since macOS 11+; standard in macOS Finder, etc) to allow even larger space around close/minimize/zoom buttons

This commit is contained in:
Karl Tauber
2023-12-15 18:21:35 +01:00
parent 13528b49cb
commit a1adde0888
6 changed files with 113 additions and 34 deletions

View File

@@ -527,21 +527,6 @@ public interface FlatClientProperties
*/ */
String WINDOW_STYLE_SMALL = "small"; String WINDOW_STYLE_SMALL = "small";
/**
* Specifies whether the window should have a large title bar.
* This adds extra space around the close/minimize/zoom buttons.
* Useful if <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">full window content</a>
* is enabled.
* <p>
* (requires macOS 10.14+, Java 17+ and client property {@code apple.awt.fullWindowContent} set to {@code true})
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3.3
*/
String MACOS_LARGE_WINDOW_TITLE_BAR = "FlatLaf.macOS.largeWindowTitleBar";
//---- JScrollBar / JScrollPane ------------------------------------------- //---- JScrollBar / JScrollPane -------------------------------------------
@@ -1278,6 +1263,46 @@ public interface FlatClientProperties
String TREE_PAINT_SELECTION = "JTree.paintSelection"; String TREE_PAINT_SELECTION = "JTree.paintSelection";
//---- macOS --------------------------------------------------------------
/**
* Specifies the style of macOS window close/minimize/zoom buttons.
* This does not change visual appearance but adds extra space around the buttons.
* Useful if <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">full window content</a>
* is enabled.
* <p>
* (requires macOS 10.14+ or 11+ for style 'large', Java 17+ and client property {@code apple.awt.fullWindowContent} set to {@code true})
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String} or {@link java.lang.Boolean}<br>
* <strong>Allowed Values</strong>
* {@link #MACOS_WINDOW_TITLE_BAR_STYLE_MEDIUM},
* {@link #MACOS_WINDOW_TITLE_BAR_STYLE_LARGE} (requires macOS 11+) or
* {@code true} (equal to 'large')
*
* @since 3.3
*/
String MACOS_WINDOW_BUTTON_STYLE = "FlatLaf.macOS.windowButtonStyle";
/**
* Add medium space around the macOS window close/minimize/zoom buttons.
*
* @see #MACOS_WINDOW_BUTTON_STYLE
* @since 3.3
*/
String MACOS_WINDOW_BUTTON_STYLE_MEDIUM = "medium";
/**
* Add large space around the macOS window close/minimize/zoom buttons.
* <p>
* (requires macOS 11+; 'medium' is used on older systems)
*
* @see #MACOS_WINDOW_BUTTON_STYLE
* @since 3.3
*/
String MACOS_WINDOW_BUTTON_STYLE_LARGE = "large";
//---- helper methods ----------------------------------------------------- //---- helper methods -----------------------------------------------------
/** /**

View File

@@ -54,7 +54,12 @@ public class FlatNativeMacLibrary
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor ); public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
public native static boolean setWindowToolbar( Window window, boolean hasToolbar ); public static final int
BUTTON_STYLE_DEFAULT = 0,
BUTTON_STYLE_MEDIUM = 1,
BUTTON_STYLE_LARGE = 2;
public native static boolean setWindowButtonStyle( Window window, int buttonStyle );
public native static int getWindowButtonAreaWidth( Window window ); public native static int getWindowButtonAreaWidth( Window window );
public native static int getWindowTitleBarHeight( Window window ); public native static int getWindowTitleBarHeight( Window window );
public native static boolean isWindowFullScreen( Window window ); public native static boolean isWindowFullScreen( Window window );

View File

@@ -368,7 +368,7 @@ public class FlatRootPaneUI
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." ); throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
break; break;
case FlatClientProperties.MACOS_LARGE_WINDOW_TITLE_BAR: case FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE:
case "ancestor": case "ancestor":
if( SystemInfo.isMacFullWindowContentSupported && if( SystemInfo.isMacFullWindowContentSupported &&
SystemInfo.isJava_17_orLater && SystemInfo.isJava_17_orLater &&
@@ -376,10 +376,21 @@ public class FlatRootPaneUI
FlatClientProperties.clientPropertyBoolean( rootPane, "apple.awt.fullWindowContent", false ) && FlatClientProperties.clientPropertyBoolean( rootPane, "apple.awt.fullWindowContent", false ) &&
FlatNativeMacLibrary.isLoaded() ) FlatNativeMacLibrary.isLoaded() )
{ {
int buttonStyle = FlatNativeMacLibrary.BUTTON_STYLE_DEFAULT;
Object value = rootPane.getClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE );
switch( String.valueOf( value ) ) {
case FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_MEDIUM:
buttonStyle = FlatNativeMacLibrary.BUTTON_STYLE_MEDIUM;
break;
case "true":
case FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_LARGE:
buttonStyle = FlatNativeMacLibrary.BUTTON_STYLE_LARGE;
break;
}
Window window = SwingUtilities.windowForComponent( rootPane ); Window window = SwingUtilities.windowForComponent( rootPane );
boolean enabled = FlatClientProperties.clientPropertyBoolean( rootPane, FlatNativeMacLibrary.setWindowButtonStyle( window, buttonStyle );
FlatClientProperties.MACOS_LARGE_WINDOW_TITLE_BAR, false );
FlatNativeMacLibrary.setWindowToolbar( window, enabled );
} }
break; break;
} }

View File

@@ -94,7 +94,7 @@ class DemoFrame
// expand window content into window title bar and make title bar transparent // expand window content into window title bar and make title bar transparent
rootPane.putClientProperty( "apple.awt.fullWindowContent", true ); rootPane.putClientProperty( "apple.awt.fullWindowContent", true );
rootPane.putClientProperty( "apple.awt.transparentTitleBar", true ); rootPane.putClientProperty( "apple.awt.transparentTitleBar", true );
rootPane.putClientProperty( FlatClientProperties.MACOS_LARGE_WINDOW_TITLE_BAR, true ); rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE, true );
// hide window title // hide window title
if( SystemInfo.isJava_17_orLater ) if( SystemInfo.isJava_17_orLater )
@@ -902,15 +902,19 @@ class DemoFrame
buttonGroup1.add(radioButtonMenuItem3); buttonGroup1.add(radioButtonMenuItem3);
// JFormDesigner - End of component initialization //GEN-END:initComponents // JFormDesigner - End of component initialization //GEN-END:initComponents
//TODO remove
backButton.addActionListener( e -> { backButton.addActionListener( e -> {
rootPane.putClientProperty( FlatClientProperties.MACOS_LARGE_WINDOW_TITLE_BAR, true ); rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE, FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_LARGE );
}); });
forwardButton.addActionListener( e -> { forwardButton.addActionListener( e -> {
rootPane.putClientProperty( FlatClientProperties.MACOS_LARGE_WINDOW_TITLE_BAR, null ); rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE, FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_MEDIUM );
});
cutButton.addActionListener( e -> {
rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE, null );
}); });
cutButton.addActionListener( e -> System.out.println( e ) ); copyButton.addActionListener( e -> System.out.println( e ) );
cutButton.addMouseListener( new MouseListener() { copyButton.addMouseListener( new MouseListener() {
@Override @Override
public void mouseReleased( MouseEvent e ) { public void mouseReleased( MouseEvent e ) {
@@ -942,7 +946,7 @@ class DemoFrame
System.out.println( "m click" ); System.out.println( "m click" );
} }
} ); } );
cutButton.addMouseMotionListener( new MouseMotionListener() { copyButton.addMouseMotionListener( new MouseMotionListener() {
@Override @Override
public void mouseMoved( MouseEvent e ) { public void mouseMoved( MouseEvent e ) {

View File

@@ -7,6 +7,12 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_DEFAULT
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_DEFAULT 0L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_MEDIUM
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_MEDIUM 1L
#undef com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_LARGE
#define com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_LARGE 2L
/* /*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary * Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: setWindowRoundedBorder * Method: setWindowRoundedBorder
@@ -17,11 +23,11 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
/* /*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary * Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary
* Method: setWindowToolbar * Method: setWindowButtonStyle
* Signature: (Ljava/awt/Window;Z)Z * Signature: (Ljava/awt/Window;I)Z
*/ */
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowToolbar JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowButtonStyle
(JNIEnv *, jclass, jobject, jboolean); (JNIEnv *, jclass, jobject, jint);
/* /*
* Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary * Class: com_formdev_flatlaf_ui_FlatNativeMacLibrary

View File

@@ -50,7 +50,7 @@ NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
if( window == NULL ) if( window == NULL )
return NULL; return NULL;
// initialize field IDs (done only once because fields are static) // initialize field IDs (done only once because variables are static)
static jfieldID peerID = getFieldID( env, "java/awt/Component", "peer", "Ljava/awt/peer/ComponentPeer;" ); static jfieldID peerID = getFieldID( env, "java/awt/Component", "peer", "Ljava/awt/peer/ComponentPeer;" );
static jfieldID platformWindowID = getFieldID( env, "sun/lwawt/LWWindowPeer", "platformWindow", "Lsun/lwawt/PlatformWindow;" ); static jfieldID platformWindowID = getFieldID( env, "sun/lwawt/LWWindowPeer", "platformWindow", "Lsun/lwawt/PlatformWindow;" );
static jfieldID ptrID = getFieldID( env, "sun/lwawt/macosx/CFRetainedResource", "ptr", "J" ); static jfieldID ptrID = getFieldID( env, "sun/lwawt/macosx/CFRetainedResource", "ptr", "J" );
@@ -121,8 +121,8 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
} }
extern "C" extern "C"
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowToolbar JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowButtonStyle
( JNIEnv* env, jclass cls, jobject window, jboolean hasToolbar ) ( JNIEnv* env, jclass cls, jobject window, jint buttonStyle )
{ {
JNI_COCOA_ENTER() JNI_COCOA_ENTER()
@@ -130,7 +130,20 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
if( nsWindow == NULL ) if( nsWindow == NULL )
return FALSE; return FALSE;
if( hasToolbar == (nsWindow.toolbar != NULL) ) #define STYLE_DEFAULT com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_DEFAULT
#define STYLE_MEDIUM com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_MEDIUM
#define STYLE_LARGE com_formdev_flatlaf_ui_FlatNativeMacLibrary_BUTTON_STYLE_LARGE
bool isMacOS_11_orLater = @available( macOS 11, * );
if( !isMacOS_11_orLater && buttonStyle == STYLE_LARGE )
buttonStyle = STYLE_MEDIUM;
int oldButtonStyle = (nsWindow.toolbar != NULL)
? ((isMacOS_11_orLater && nsWindow.toolbarStyle == NSWindowToolbarStyleUnified)
? STYLE_LARGE
: STYLE_MEDIUM)
: STYLE_DEFAULT;
if( buttonStyle == oldButtonStyle )
return TRUE; return TRUE;
WindowData* windowData = getWindowData( nsWindow, true ); WindowData* windowData = getWindowData( nsWindow, true );
@@ -140,12 +153,26 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
// add/remove toolbar // add/remove toolbar
NSToolbar* toolbar = NULL; NSToolbar* toolbar = NULL;
bool hasToolbar = (buttonStyle != STYLE_DEFAULT);
if( hasToolbar ) { if( hasToolbar ) {
toolbar = [NSToolbar new]; toolbar = [NSToolbar new];
toolbar.showsBaselineSeparator = NO; // necessary for older macOS versions toolbar.showsBaselineSeparator = NO; // necessary for older macOS versions
if( isWindowFullScreen( nsWindow ) )
toolbar.visible = NO;
} }
nsWindow.toolbar = toolbar; nsWindow.toolbar = toolbar;
if( isMacOS_11_orLater ) {
nsWindow.toolbarStyle = (buttonStyle == STYLE_LARGE)
? NSWindowToolbarStyleUnified
: (buttonStyle == STYLE_MEDIUM)
? NSWindowToolbarStyleUnifiedCompact
: NSWindowToolbarStyleAutomatic;
}
windowData.lastWindowButtonAreaWidth = 0;
windowData.lastWindowTitleBarHeight = 0;
// NSLog( @"\n%@\n\n", [nsWindow.contentView.superview _subtreeDescription] ); // NSLog( @"\n%@\n\n", [nsWindow.contentView.superview _subtreeDescription] );
// when window becomes full screen, it is necessary to hide the toolbar // when window becomes full screen, it is necessary to hide the toolbar
@@ -161,6 +188,7 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
// remembar title bar height so that "main" JToolBar keeps its height in full screen // remembar title bar height so that "main" JToolBar keeps its height in full screen
windowData.lastWindowButtonAreaWidth = getWindowButtonAreaWidth( nsWindow ); windowData.lastWindowButtonAreaWidth = getWindowButtonAreaWidth( nsWindow );
windowData.lastWindowTitleBarHeight = getWindowTitleBarHeight( nsWindow ); windowData.lastWindowTitleBarHeight = getWindowTitleBarHeight( nsWindow );
// NSLog(@"%d %d",windowData.lastWindowButtonAreaWidth,windowData.lastWindowTitleBarHeight);
nsWindow.toolbar.visible = NO; nsWindow.toolbar.visible = NO;
} }