macOS large title bar: fixed wrong "main" JToolBar height and left inset after leaving full screen

This commit is contained in:
Karl Tauber
2023-12-14 18:51:03 +01:00
parent f3be3f2d1c
commit 13528b49cb
6 changed files with 101 additions and 37 deletions

View File

@@ -54,7 +54,7 @@ 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 void setWindowToolbar( Window window, boolean hasToolbar ); public native static boolean setWindowToolbar( Window window, boolean hasToolbar );
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

@@ -116,11 +116,7 @@ public class FlatToolBarBorder
} }
// on macOS, add some extra space to left side for close/minimize/zoom buttons (if necessary) // on macOS, add some extra space to left side for close/minimize/zoom buttons (if necessary)
if( c instanceof JToolBar && if( c instanceof JToolBar && FlatToolBarUI.isMacOSMainToolbar( (JToolBar) c ) ) {
FlatToolBarUI.isMacOSMainToolbar( (JToolBar) c ) &&
(!FlatNativeMacLibrary.isLoaded() ||
!FlatNativeMacLibrary.isWindowFullScreen( SwingUtilities.windowForComponent( c ) )) )
{
// get button area width from macOS // get button area width from macOS
int buttonBarWidth = FlatNativeMacLibrary.isLoaded() int buttonBarWidth = FlatNativeMacLibrary.isLoaded()
? FlatNativeMacLibrary.getWindowButtonAreaWidth( SwingUtilities.windowForComponent( c ) ) ? FlatNativeMacLibrary.getWindowButtonAreaWidth( SwingUtilities.windowForComponent( c ) )

View File

@@ -18,9 +18,9 @@ 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: setWindowToolbar
* Signature: (Ljava/awt/Window;Z)V * Signature: (Ljava/awt/Window;Z)Z
*/ */
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowToolbar JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowToolbar
(JNIEnv *, jclass, jobject, jboolean); (JNIEnv *, jclass, jobject, jboolean);
/* /*

View File

@@ -25,6 +25,27 @@
* @author Karl Tauber * @author Karl Tauber
*/ */
@interface WindowData : NSObject
// used when window is full screen
@property (nonatomic) int lastWindowButtonAreaWidth;
@property (nonatomic) int lastWindowTitleBarHeight;
// full screen observers
@property (nonatomic) id willEnterFullScreenObserver;
@property (nonatomic) id didExitFullScreenObserver;
@end
@implementation WindowData
@end
// declare internal methods
NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window );
WindowData* getWindowData( NSWindow* nsWindow, bool allocate );
int getWindowButtonAreaWidth( NSWindow* nsWindow );
int getWindowTitleBarHeight( NSWindow* nsWindow );
bool isWindowFullScreen( NSWindow* nsWindow );
NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) { NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
if( window == NULL ) if( window == NULL )
return NULL; return NULL;
@@ -50,6 +71,16 @@ NSWindow* getNSWindow( JNIEnv* env, jclass cls, jobject window ) {
return (NSWindow *) jlong_to_ptr( env->GetLongField( platformWindow, ptrID ) ); return (NSWindow *) jlong_to_ptr( env->GetLongField( platformWindow, ptrID ) );
} }
WindowData* getWindowData( NSWindow* nsWindow, bool allocate ) {
static char key;
WindowData* windowData = objc_getAssociatedObject( nsWindow, &key );
if( windowData == NULL && allocate ) {
windowData = [WindowData new];
objc_setAssociatedObject( nsWindow, &key, windowData, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
}
return windowData;
}
extern "C" extern "C"
JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowRoundedBorder
( JNIEnv* env, jclass cls, jobject window, jfloat radius, jfloat borderWidth, jint borderColor ) ( JNIEnv* env, jclass cls, jobject window, jfloat radius, jfloat borderWidth, jint borderColor )
@@ -90,20 +121,22 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setW
} }
extern "C" extern "C"
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowToolbar JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindowToolbar
( JNIEnv* env, jclass cls, jobject window, jboolean hasToolbar ) ( JNIEnv* env, jclass cls, jobject window, jboolean hasToolbar )
{ {
JNI_COCOA_ENTER() JNI_COCOA_ENTER()
NSWindow* nsWindow = getNSWindow( env, cls, window ); NSWindow* nsWindow = getNSWindow( env, cls, window );
if( nsWindow == NULL ) if( nsWindow == NULL )
return; return FALSE;
if( hasToolbar == (nsWindow.toolbar != NULL) ) if( hasToolbar == (nsWindow.toolbar != NULL) )
return; return TRUE;
WindowData* windowData = getWindowData( nsWindow, true );
[FlatJNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ [FlatJNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
NSLog( @"\n%@\n\n", [nsWindow.contentView.superview _subtreeDescription] ); // NSLog( @"\n%@\n\n", [nsWindow.contentView.superview _subtreeDescription] );
// add/remove toolbar // add/remove toolbar
NSToolbar* toolbar = NULL; NSToolbar* toolbar = NULL;
@@ -113,45 +146,51 @@ JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_setWindo
} }
nsWindow.toolbar = toolbar; nsWindow.toolbar = toolbar;
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
// because it otherwise is shown non-transparent and hides Swing components // because it otherwise is shown non-transparent and hides Swing components
static char enterObserverKey;
static char exitObserverKey;
NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
if( hasToolbar ) { if( hasToolbar ) {
NSLog( @"add observers %@", nsWindow ); // NSLog( @"add observers %@", nsWindow );
id enterObserver = [center addObserverForName:NSWindowWillEnterFullScreenNotification windowData.willEnterFullScreenObserver = [center addObserverForName:NSWindowWillEnterFullScreenNotification
object:nsWindow queue:nil usingBlock:^(NSNotification *note) { object:nsWindow queue:nil usingBlock:^(NSNotification *note) {
NSLog( @"enter full screen %@", nsWindow ); // NSLog( @"enter full screen %@", nsWindow );
if( nsWindow.toolbar != NULL ) if( nsWindow.toolbar != NULL ) {
// remember button area width, which is used later when window exits full screen
// remembar title bar height so that "main" JToolBar keeps its height in full screen
windowData.lastWindowButtonAreaWidth = getWindowButtonAreaWidth( nsWindow );
windowData.lastWindowTitleBarHeight = getWindowTitleBarHeight( nsWindow );
nsWindow.toolbar.visible = NO; nsWindow.toolbar.visible = NO;
}
}]; }];
id exitObserver = [center addObserverForName:NSWindowDidExitFullScreenNotification windowData.didExitFullScreenObserver = [center addObserverForName:NSWindowDidExitFullScreenNotification
object:nsWindow queue:nil usingBlock:^(NSNotification *note) { object:nsWindow queue:nil usingBlock:^(NSNotification *note) {
NSLog( @"exit full screen %@", nsWindow ); // NSLog( @"exit full screen %@", nsWindow );
if( nsWindow.toolbar != NULL ) if( nsWindow.toolbar != NULL )
nsWindow.toolbar.visible = YES; nsWindow.toolbar.visible = YES;
windowData.lastWindowButtonAreaWidth = 0;
windowData.lastWindowTitleBarHeight = 0;
}]; }];
objc_setAssociatedObject( nsWindow, &enterObserverKey, enterObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
objc_setAssociatedObject( nsWindow, &exitObserverKey, exitObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
} else { } else {
NSLog( @"remove observers %@", nsWindow ); // NSLog( @"remove observers %@", nsWindow );
id enterObserver = objc_getAssociatedObject( nsWindow, &enterObserverKey ); if( windowData.willEnterFullScreenObserver != NULL ) {
id exitObserver = objc_getAssociatedObject( nsWindow, &exitObserverKey ); [center removeObserver:windowData.willEnterFullScreenObserver];
if( enterObserver != NULL ) { windowData.willEnterFullScreenObserver = nil;
[center removeObserver:enterObserver];
objc_setAssociatedObject( nsWindow, &enterObserverKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
} }
if( exitObserver != NULL ) { if( windowData.didExitFullScreenObserver != NULL ) {
[center removeObserver:exitObserver]; [center removeObserver:windowData.didExitFullScreenObserver];
objc_setAssociatedObject( nsWindow, &exitObserverKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC ); windowData.didExitFullScreenObserver = nil;
} }
} }
}]; }];
return TRUE;
JNI_COCOA_EXIT() JNI_COCOA_EXIT()
return FALSE;
} }
extern "C" extern "C"
@@ -164,6 +203,23 @@ JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWindo
if( nsWindow == NULL ) if( nsWindow == NULL )
return -1; return -1;
// return zero if window is full screen because close/minimize/zoom buttons are hidden
if( isWindowFullScreen( nsWindow ) )
return 0;
// use remembered value if window is in transition from full screen to non-full screen
// because NSToolbar is not yet visible
WindowData* windowData = getWindowData( nsWindow, false );
if( windowData != NULL && windowData.lastWindowButtonAreaWidth > 0 )
return windowData.lastWindowButtonAreaWidth;
return getWindowButtonAreaWidth( nsWindow );
JNI_COCOA_EXIT()
return -1;
}
int getWindowButtonAreaWidth( NSWindow* nsWindow ) {
// get buttons // get buttons
NSView* buttons[3] = { NSView* buttons[3] = {
[nsWindow standardWindowButton:NSWindowCloseButton], [nsWindow standardWindowButton:NSWindowCloseButton],
@@ -193,8 +249,6 @@ JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWindo
// 'right' is the actual button area width (from left window edge) // 'right' is the actual button area width (from left window edge)
// adding 'left' to add same empty space on right side as on left side // adding 'left' to add same empty space on right side as on left side
return right + left; return right + left;
JNI_COCOA_EXIT()
} }
extern "C" extern "C"
@@ -207,14 +261,24 @@ JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_getWindo
if( nsWindow == NULL ) if( nsWindow == NULL )
return -1; return -1;
// use remembered value if window is full screen because NSToolbar is hidden
WindowData* windowData = getWindowData( nsWindow, false );
if( windowData != NULL && windowData.lastWindowTitleBarHeight > 0 )
return windowData.lastWindowTitleBarHeight;
return getWindowTitleBarHeight( nsWindow );
JNI_COCOA_EXIT()
return -1;
}
int getWindowTitleBarHeight( NSWindow* nsWindow ) {
NSView* closeButton = [nsWindow standardWindowButton:NSWindowCloseButton]; NSView* closeButton = [nsWindow standardWindowButton:NSWindowCloseButton];
if( closeButton == NULL ) if( closeButton == NULL )
return -1; return -1;
NSView* titlebar = closeButton.superview; NSView* titlebar = closeButton.superview;
return titlebar.bounds.size.height; return titlebar.bounds.size.height;
JNI_COCOA_EXIT()
} }
extern "C" extern "C"
@@ -227,8 +291,12 @@ JNIEXPORT jboolean JNICALL Java_com_formdev_flatlaf_ui_FlatNativeMacLibrary_isWi
if( nsWindow == NULL ) if( nsWindow == NULL )
return FALSE; return FALSE;
return (jboolean) (([nsWindow styleMask] & NSWindowStyleMaskFullScreen) != 0); return (jboolean) isWindowFullScreen( nsWindow );
JNI_COCOA_EXIT() JNI_COCOA_EXIT()
return FALSE;
} }
bool isWindowFullScreen( NSWindow* nsWindow ) {
return ((nsWindow.styleMask & NSWindowStyleMaskFullScreen) != 0);
}