JBR-5174 Opening project as tabs in Mac OS (version2)

JBR-5023 Configure TabbingIdentifier during create native window
JBR-5256 IDEA window resizes to zero height when exit full-screen mode with new window controls enabled
JBR-5197 Window control buttons are not visible in full-screen mode in dark themes when IDE window is focused
JBR-5175 jb/java/awt/Window/FullScreenTwoFrames.java: -[AWTWindow resetWindowFullScreeControls]: unrecognized selector sent to instance 0x60000232d5f0
JBR-5499 Window control buttons bugfix
JBR-4462 BigSur: project tab does not gain focus when click it after focusing another app
This commit is contained in:
Alexander Lobas
2022-05-11 19:40:30 +03:00
committed by Nikita Gubarkov
parent 54f70cfc23
commit 540d5a32c4
5 changed files with 343 additions and 2 deletions

View File

@@ -991,6 +991,14 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
return ref.get();
}
private boolean isTabbedWindow() {
AtomicBoolean ref = new AtomicBoolean();
execute(ptr -> {
ref.set(CWrapper.NSWindow.isTabbedWindow(ptr));
});
return ref.get();
}
// We want a window to be always shown at the same space as its owning window.
// But macOS doesn't have an API to control the target space for a window -
// it's always shown at the active space. So if the target space isn't active now,
@@ -1353,7 +1361,9 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
// which is going to become 'main window', are placed above their siblings.
CPlatformWindow rootOwner = getRootOwner();
if (rootOwner.isVisible() && !rootOwner.isIconified() && !rootOwner.isActive()) {
rootOwner.execute(CWrapper.NSWindow::orderFrontIfOnActiveSpace);
if (rootOwner != this || !isTabbedWindow()) {
rootOwner.execute(CWrapper.NSWindow::orderFrontIfOnActiveSpace);
}
}
// Do not order child windows of iconified owner.

View File

@@ -108,6 +108,8 @@ final class CWrapper {
static native void zoom(long window);
static native void makeFirstResponder(long window, long responder);
static native boolean isTabbedWindow(long window);
}
static final class NSView {

View File

@@ -50,6 +50,10 @@
NSWindowTabbingMode javaWindowTabbingMode;
BOOL isEnterFullScreen;
CGFloat _customTitleBarHeight;
BOOL hideTabController;
NSView *_fullScreenButtons;
NSView *_fullScreenOriginalButtons;
}
// An instance of either AWTWindow_Normal or AWTWindow_Panel
@@ -74,6 +78,7 @@
@property (nonatomic, retain) NSMutableArray *customTitleBarConstraints;
@property (nonatomic, retain) NSLayoutConstraint *customTitleBarHeightConstraint;
@property (nonatomic, retain) NSMutableArray *customTitleBarButtonCenterXConstraints;
@property (nonatomic) BOOL hideTabController;
- (id) initWithPlatformWindow:(jobject)javaPlatformWindow
ownerWindow:owner
@@ -116,4 +121,13 @@
- (id) initWithPlatformWindow:(jobject)javaPlatformWindow;
@end
@interface AWTButtonsView : NSView {
@private BOOL _showButtons;
NSColor* _color;
}
- (void)configureColors;
@end
#endif _AWTWINDOW_H

View File

@@ -38,6 +38,7 @@
#import "ThreadUtilities.h"
#import "NSApplicationAWT.h"
#import "JNIUtilities.h"
#import "PropertiesUtilities.h"
#define MASK(KEY) \
(sun_lwawt_macosx_CPlatformWindow_ ## KEY)
@@ -55,6 +56,14 @@ static jclass jc_CPlatformWindow = NULL;
#define GET_CPLATFORM_WINDOW_CLASS_RETURN(ret) \
GET_CLASS_RETURN(jc_CPlatformWindow, "sun/lwawt/macosx/CPlatformWindow", ret);
@interface NSTitlebarAccessoryViewController (Private)
- (void)_setHidden:(BOOL)h animated:(BOOL)a;
@end
@interface NSWindow (Private)
- (void)_setTabBarAccessoryViewController:(id)controller;
@end
// Cocoa windowDidBecomeKey/windowDidResignKey notifications
// doesn't provide information about "opposite" window, so we
// have to do a bit of tracking. This variable points to a window
@@ -348,6 +357,26 @@ AWT_NS_WINDOW_IMPLEMENTATION
[super orderOut:sender];
}
- (void)_setTabBarAccessoryViewController:(id)_controller {
if (((AWTWindow *)self.delegate).hideTabController) {
NSTitlebarAccessoryViewController* controller = [[NSTitlebarAccessoryViewController alloc] init];
controller.view = [[NSView alloc] init];
[controller.view setFrame:NSMakeRect(0, 0, 0, 0)];
[controller _setHidden:YES animated:NO];
[super _setTabBarAccessoryViewController:controller];
} else {
[super _setTabBarAccessoryViewController:_controller];
}
}
- (BOOL)isNativeSelected {
if (@available(macOS 10.13, *)) {
return [[self tabGroup] selectedWindow] == self;
}
return NO;
}
@end
@implementation AWTWindow_Panel
AWT_NS_WINDOW_IMPLEMENTATION
@@ -372,6 +401,7 @@ AWT_NS_WINDOW_IMPLEMENTATION
@synthesize isJustCreated;
@synthesize javaWindowTabbingMode;
@synthesize isEnterFullScreen;
@synthesize hideTabController;
- (void) updateMinMaxSize:(BOOL)resizable {
if (resizable) {
@@ -547,6 +577,8 @@ AWT_ASSERT_APPKIT_THREAD;
self.nsWindow.collectionBehavior = NSWindowCollectionBehaviorManaged;
self.isEnterFullScreen = NO;
[self configureJavaWindowTabbingIdentifier];
if (self.isCustomTitleBarEnabled && !self.isFullScreen) {
[self setUpCustomTitleBar];
}
@@ -569,6 +601,64 @@ AWT_ASSERT_APPKIT_THREAD;
return [self.nsWindow windowNumber] == [AWTWindow getTopmostWindowUnderMouseID];
}
- (void) configureJavaWindowTabbingIdentifier {
AWT_ASSERT_APPKIT_THREAD;
self.hideTabController = NO;
if (self.javaWindowTabbingMode != NSWindowTabbingModeAutomatic) {
return;
}
JNIEnv *env = [ThreadUtilities getJNIEnv];
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
if (platformWindow == NULL) {
return;
}
GET_CPLATFORM_WINDOW_CLASS();
DECLARE_FIELD(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
jobject awtWindow = (*env)->GetObjectField(env, platformWindow, jf_target);
if (awtWindow != NULL) {
DECLARE_CLASS(jc_RootPaneContainer, "javax/swing/RootPaneContainer");
if ((*env)->IsInstanceOf(env, awtWindow, jc_RootPaneContainer)) {
DECLARE_METHOD(jm_getRootPane, jc_RootPaneContainer, "getRootPane", "()Ljavax/swing/JRootPane;");
jobject rootPane = (*env)->CallObjectMethod(env, awtWindow, jm_getRootPane);
CHECK_EXCEPTION();
if (rootPane != NULL) {
DECLARE_CLASS(jc_JComponent, "javax/swing/JComponent");
DECLARE_METHOD(jm_getClientProperty, jc_JComponent, "getClientProperty", "(Ljava/lang/Object;)Ljava/lang/Object;");
jstring jKey = NSStringToJavaString(env, @"JavaWindowTabbingIdentifier");
jobject jValue = (*env)->CallObjectMethod(env, rootPane, jm_getClientProperty, jKey);
CHECK_EXCEPTION();
if (jValue != NULL) {
DECLARE_CLASS(jc_String, "java/lang/String");
if ((*env)->IsInstanceOf(env, jValue, jc_String)) {
NSString *winId = JavaStringToNSString(env, (jstring)jValue);
[self.nsWindow setTabbingIdentifier:winId];
if ([winId characterAtIndex:0] == '+') {
self.hideTabController = YES;
[self.nsWindow _setTabBarAccessoryViewController:nil];
}
}
(*env)->DeleteLocalRef(env, jValue);
}
(*env)->DeleteLocalRef(env, jKey);
(*env)->DeleteLocalRef(env, rootPane);
}
}
(*env)->DeleteLocalRef(env, awtWindow);
}
(*env)->DeleteLocalRef(env, platformWindow);
}
- (NSWindowTabbingMode) getJavaWindowTabbingMode {
AWT_ASSERT_APPKIT_THREAD;
@@ -1309,6 +1399,15 @@ AWT_ASSERT_APPKIT_THREAD;
if (self.isCustomTitleBarEnabled) {
[self forceHideCustomTitleBarTitle:NO];
[self updateCustomTitleBarInsets:NO];
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
NSString *newFullScreenControls1 = [PropertiesUtilities
javaSystemPropertyForKey:@"apple.awt.newFullScreeControls" withEnv:env];
NSString *newFullScreenControls2 = [PropertiesUtilities
javaSystemPropertyForKey:@"apple.awt.newFullScreenControls" withEnv:env];
if ([@"true" isCaseInsensitiveLike:newFullScreenControls1] || [@"true" isCaseInsensitiveLike:newFullScreenControls2]) {
[self setWindowFullScreenControls];
}
}
[self allowMovingChildrenBetweenSpaces:NO];
[self fullScreenTransitionFinished];
@@ -1355,6 +1454,8 @@ AWT_ASSERT_APPKIT_THREAD;
}
- (void)windowDidExitFullScreen:(NSNotification *)notification {
[self resetWindowFullScreenControls];
self.isEnterFullScreen = NO;
[self fullScreenTransitionFinished];
@@ -1464,7 +1565,9 @@ static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
}
- (void) setUpCustomTitleBar {
if (self.customTitleBarConstraints != nil) {
[self resetCustomTitleBar];
}
/**
* The view hierarchy normally looks as follows:
* NSThemeFrame
@@ -1560,6 +1663,8 @@ static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
}];
[self setWindowControlsHidden:!self.customTitleBarControlsVisible];
[self updateCustomTitleBarInsets:self.customTitleBarControlsVisible];
[self updateFullScreenButtons];
}
- (void) resetCustomTitleBar {
@@ -1598,11 +1703,104 @@ static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
}
- (void) setWindowControlsHidden: (BOOL) hidden {
if (_fullScreenOriginalButtons != nil) {
[_fullScreenOriginalButtons.window.contentView setHidden:NO];
_fullScreenButtons.hidden = YES;
}
[self.nsWindow standardWindowButton:NSWindowCloseButton].hidden = hidden;
[self.nsWindow standardWindowButton:NSWindowZoomButton].hidden = hidden;
[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton].hidden = hidden;
}
- (void) setWindowFullScreenControls {
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
NSString *dfmMode = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.distraction.free.mode" withEnv:env];
if ([@"true" isCaseInsensitiveLike:dfmMode]) {
return;
}
NSView* oldCloseButton = [self.nsWindow standardWindowButton:NSWindowCloseButton];
_fullScreenOriginalButtons = oldCloseButton.superview;
NSRect closeButtonRect = [oldCloseButton frame];
NSRect miniaturizeButtonRect = [[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton] frame];
NSRect zoomButtonRect = [[self.nsWindow standardWindowButton:NSWindowZoomButton] frame];
for (NSWindow* window in [[NSApplication sharedApplication] windows]) {
if ([window isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")]) {
[window.contentView setHidden:YES];
}
}
_fullScreenButtons = [[AWTButtonsView alloc] init];
[self updateFullScreenButtons];
[_fullScreenButtons addTrackingArea:[[NSTrackingArea alloc] initWithRect:[_fullScreenButtons visibleRect]
options:(NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved)
owner:_fullScreenButtons userInfo:nil]];
NSUInteger masks = [self.nsWindow styleMask];
NSButton *closeButton = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:masks];
[closeButton setFrame:closeButtonRect];
[_fullScreenButtons addSubview:closeButton];
NSButton *miniaturizeButton = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:masks];
[miniaturizeButton setFrame:miniaturizeButtonRect];
[_fullScreenButtons addSubview:miniaturizeButton];
NSButton *zoomButton = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:masks];
[zoomButton setFrame:zoomButtonRect];
[_fullScreenButtons addSubview:zoomButton];
[self.nsWindow.contentView addSubview:_fullScreenButtons];
[self updateColors];
}
- (void)updateColors {
if (_fullScreenButtons != nil) {
[(AWTButtonsView *)_fullScreenButtons configureColors];
}
}
- (void)updateFullScreenButtons {
if (_fullScreenButtons == nil || _fullScreenOriginalButtons == nil) {
return;
}
NSView *parent = self.nsWindow.contentView;
CGFloat w = 80;
CGFloat h = _fullScreenOriginalButtons.frame.size.height;
CGFloat x = 6;
CGFloat y = parent.frame.size.height - h - (self.customTitleBarHeight - h) / 2.0;
[_fullScreenButtons setFrame:NSMakeRect(x, y, w - x, h)];
}
- (void)updateFullScreenButtons: (BOOL) dfm {
if (dfm) {
if (_fullScreenButtons == nil || _fullScreenOriginalButtons == nil) {
return;
}
[_fullScreenOriginalButtons.window.contentView setHidden:NO];
[self resetWindowFullScreenControls];
} else {
if (!self.isCustomTitleBarEnabled || _fullScreenButtons != nil) {
return;
}
[self setWindowFullScreenControls];
}
}
- (void) resetWindowFullScreenControls {
if (_fullScreenButtons != nil) {
[_fullScreenButtons removeFromSuperview];
_fullScreenButtons = nil;
_fullScreenOriginalButtons = nil;
}
}
- (BOOL) isFullScreen {
NSUInteger masks = [self.nsWindow styleMask];
return (masks & NSWindowStyleMaskFullScreen) != 0;
@@ -1648,6 +1846,8 @@ static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
} else {
[self resetCustomTitleBar];
}
} else {
[self updateFullScreenButtons];
}
} else if (enabled) {
[self updateCustomTitleBarConstraints];
@@ -1766,6 +1966,96 @@ static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
@end
@implementation AWTButtonsView
- (void)dealloc {
[_color release];
[super dealloc];
}
- (void)mouseEntered:(NSEvent *)theEvent {
[self updateButtons:YES];
}
- (void)mouseExited:(NSEvent *)theEvent {
[self updateButtons:NO];
}
- (void)configureColors {
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
NSString *javaColor = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.newFullScreeControls.background" withEnv:env];
[_color release];
if (javaColor == nil) {
_color = nil;
} else {
int rgb = [javaColor intValue];
CGFloat alpha = (((rgb >> 24) & 0xff) / 255.0);
CGFloat red = (((rgb >> 16) & 0xff) / 255.0);
CGFloat green = (((rgb >> 8) & 0xff) / 255.0);
CGFloat blue = (((rgb >> 0) & 0xff) / 255.0);
_color = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];
[_color retain];
}
[self updateButtons:NO];
}
- (void)updateButtons:(BOOL) flag {
_showButtons = flag;
if (self.subviews.count == 3) {
[self updateButton:0 flag:flag]; // close
[self updateButton:1 flag:NO]; // miniaturize
[self updateButton:2 flag:flag]; // zoom
}
[self setNeedsDisplay:YES];
}
- (void)updateButton: (int)index flag:(BOOL) flag {
NSButton *button = (NSButton*)self.subviews[index];
[button setHidden:!flag];
[button setHighlighted:flag];
}
- (void)drawRect: (NSRect)dirtyRect {
if (self.subviews.count != 3) {
return;
}
if (_color == nil) {
[[NSColor whiteColor] setFill];
} else {
[_color setFill];
}
if (_showButtons) {
[self drawButton:1]; // miniaturize
} else {
for (int i = 0; i < 3; i++) {
[self drawButton:i];
}
}
}
- (void)drawButton: (int)index {
NSButton *button = (NSButton*)self.subviews[index];
NSRect rect = button.frame;
CGFloat r = 12;
CGFloat x = rect.origin.x + (rect.size.width - r) / 2;
CGFloat y = rect.origin.y + (rect.size.height - r) / 2;
NSBezierPath* circlePath = [NSBezierPath bezierPath];
[circlePath appendBezierPathWithOvalInRect:CGRectMake(x, y, r, r)];
[circlePath fill];
}
@end
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeSetAllAllowAutomaticTabbingProperty

View File

@@ -516,6 +516,31 @@ JNI_COCOA_ENTER(env);
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
* Method: isTabbedWindow
* Signature: (J)Z
*/
JNIEXPORT jboolean JNICALL
Java_sun_lwawt_macosx_CWrapper_00024NSWindow_isTabbedWindow
(JNIEnv *env, jclass cls, jlong windowPtr)
{
__block jboolean isTabbedWindow = JNI_FALSE;
JNI_COCOA_ENTER(env);
if (@available(macOS 10.13, *)) {
NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
isTabbedWindow = [[[window tabGroup] windows] count] > 1;
}];
}
JNI_COCOA_EXIT(env);
return isTabbedWindow;
}
/*
* Class: sun_lwawt_macosx_CWrapper$NSView
* Method: addSubview