mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-3611, JBR-3633, JBR-3666, JBR-3663, JBR-3671, JBR-3673, JBR-4181, JBR-4186, JBR-4893 Interoperability with macOS desktop spaces
(cherry-picked from commits43fdd6cd26,75335543f2,a156c6b9bf,9fdc75969b,1dcc612a81,93588d0738,94a3885bbe,c040e05703,67b6cd871f,9040fd56cd,6349b86b7f) (cherry picked from commit4fb4d51b89)
This commit is contained in:
@@ -35,6 +35,7 @@ import java.awt.FontMetrics;
|
||||
import java.awt.Frame;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.Insets;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.MenuBar;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
@@ -43,6 +44,7 @@ import java.awt.Window;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowStateListener;
|
||||
import java.awt.peer.ComponentPeer;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -98,6 +100,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
private static native void nativeExitFullScreenMode(long nsWindowPtr);
|
||||
static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
|
||||
private static native void nativeRaiseLevel(long nsWindowPtr, boolean popup, boolean onlyIfParentIsActive);
|
||||
private static native boolean nativeDelayShowing(long nsWindowPtr);
|
||||
|
||||
// Logger to report issues happened during execution but that do not affect functionality
|
||||
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow");
|
||||
@@ -684,6 +687,16 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
private static LWWindowPeer getBlockerFor(Window window) {
|
||||
if (window != null) {
|
||||
ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(window);
|
||||
if (peer instanceof LWWindowPeer) {
|
||||
return ((LWWindowPeer)peer).getBlocker();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override // PlatformWindow
|
||||
public void setVisible(boolean visible) {
|
||||
// Configure stuff
|
||||
@@ -736,7 +749,17 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
|
||||
// Actually show or hide the window
|
||||
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
|
||||
if (blocker == null || !visible) {
|
||||
if (visible && delayShowing()) {
|
||||
if (blocker == null) {
|
||||
Window focusedWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
|
||||
LWWindowPeer focusedWindowBlocker = getBlockerFor(focusedWindow);
|
||||
if (focusedWindowBlocker == peer) {
|
||||
// try to switch to target space if we're adding a modal dialog
|
||||
// that would block currently focused window
|
||||
owner.execute(CWrapper.NSWindow::orderFront);
|
||||
}
|
||||
}
|
||||
} else if (blocker == null || !visible) {
|
||||
// If it ain't blocked, or is being hidden, go regular way
|
||||
if (visible) {
|
||||
contentView.execute(viewPtr -> {
|
||||
@@ -803,7 +826,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
// Manage parent-child relationship when showing
|
||||
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
|
||||
|
||||
if (visible) {
|
||||
if (visible && !delayShowing()) {
|
||||
// Order myself above my parent
|
||||
if (owner != null && owner.isVisible()) {
|
||||
owner.execute(ownerPtr -> {
|
||||
@@ -835,7 +858,10 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
// Deal with the blocker of the window being shown
|
||||
if (blocker != null && visible) {
|
||||
// Make sure the blocker is above its siblings
|
||||
((CPlatformWindow)blocker.getPlatformWindow()).orderAboveSiblings();
|
||||
CPlatformWindow blockerWindow = (CPlatformWindow) blocker.getPlatformWindow();
|
||||
if (!blockerWindow.delayShowing()) {
|
||||
blockerWindow.orderAboveSiblings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,6 +896,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
|
||||
@Override // PlatformWindow
|
||||
public void toFront() {
|
||||
if (delayShowing()) return;
|
||||
LWCToolkit lwcToolkit = (LWCToolkit) Toolkit.getDefaultToolkit();
|
||||
Window w = DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
|
||||
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
|
||||
@@ -923,6 +950,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
|
||||
@Override
|
||||
public boolean requestWindowFocus() {
|
||||
if (delayShowing()) return false;
|
||||
execute(ptr -> {
|
||||
if (CWrapper.NSWindow.canBecomeMainWindow(ptr)) {
|
||||
CWrapper.NSWindow.makeMainWindow(ptr);
|
||||
@@ -941,6 +969,17 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
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,
|
||||
// the only way to achieve our goal seems to be delaying the appearance of the
|
||||
// window till the target space becomes active.
|
||||
private boolean delayShowing() {
|
||||
AtomicBoolean ref = new AtomicBoolean(false);
|
||||
execute(ptr -> ref.set(nativeDelayShowing(ptr)));
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFocusableWindowState() {
|
||||
setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, getFocusableStyleBits()); // set both bits at once
|
||||
@@ -1265,29 +1304,32 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
isFocusable ? focusableStyleBits : 0); // set both bits at once
|
||||
}
|
||||
|
||||
private boolean checkBlockingAndOrder() {
|
||||
private void checkBlockingAndOrder() {
|
||||
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
|
||||
if (blocker == null) {
|
||||
return false;
|
||||
// If it's not blocked, make sure it's above its siblings
|
||||
orderAboveSiblings();
|
||||
return;
|
||||
}
|
||||
|
||||
if (blocker instanceof CPrinterDialogPeer) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
CPlatformWindow pWindow = (CPlatformWindow)blocker.getPlatformWindow();
|
||||
|
||||
pWindow.orderAboveSiblings();
|
||||
if (!pWindow.delayShowing()) {
|
||||
pWindow.orderAboveSiblings();
|
||||
|
||||
pWindow.execute(ptr -> {
|
||||
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
logger.fine("Focus blocker " + Long.toHexString(ptr));
|
||||
}
|
||||
CWrapper.NSWindow.orderFrontRegardless(ptr);
|
||||
CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
|
||||
CWrapper.NSWindow.makeMainWindow(ptr);
|
||||
});
|
||||
return true;
|
||||
pWindow.execute(ptr -> {
|
||||
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
logger.fine("Focus blocker " + Long.toHexString(ptr));
|
||||
}
|
||||
CWrapper.NSWindow.orderFrontRegardless(ptr);
|
||||
CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
|
||||
CWrapper.NSWindow.makeMainWindow(ptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIconified() {
|
||||
@@ -1325,7 +1367,7 @@ 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::orderFront);
|
||||
rootOwner.execute(CWrapper.NSWindow::orderFrontIfOnActiveSpace);
|
||||
}
|
||||
|
||||
// Do not order child windows of iconified owner.
|
||||
@@ -1349,7 +1391,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
if (p instanceof LWWindowPeer) {
|
||||
CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
|
||||
iconified = isIconified();
|
||||
if (pw != null && pw.isVisible() && !iconified) {
|
||||
if (pw != null && pw.isVisible() && !iconified && !pw.delayShowing()) {
|
||||
// If the window is one of ancestors of 'main window' or is going to become main by itself,
|
||||
// the window should be ordered above its siblings; otherwise the window is just ordered
|
||||
// above its nearest parent.
|
||||
@@ -1361,7 +1403,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
}
|
||||
pwUnder.execute(underPtr -> {
|
||||
pw.execute(ptr -> {
|
||||
CWrapper.NSWindow.orderWindow(ptr, CWrapper.NSWindow.NSWindowAbove, underPtr);
|
||||
CWrapper.NSWindow.orderWindowIfOnActiveSpace(ptr, CWrapper.NSWindow.NSWindowAbove, underPtr);
|
||||
});
|
||||
});
|
||||
pwUnder = pw;
|
||||
@@ -1421,9 +1463,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
|
||||
private void windowDidBecomeMain() {
|
||||
lastBecomeMainTime = System.currentTimeMillis();
|
||||
if (checkBlockingAndOrder()) return;
|
||||
// If it's not blocked, make sure it's above its siblings
|
||||
orderAboveSiblings();
|
||||
checkBlockingAndOrder();
|
||||
}
|
||||
|
||||
private void windowWillEnterFullScreen() {
|
||||
|
||||
@@ -56,8 +56,10 @@ final class CWrapper {
|
||||
static native boolean isKeyWindow(long window);
|
||||
|
||||
static native void orderFront(long window);
|
||||
static native void orderFrontIfOnActiveSpace(long window);
|
||||
static native void orderFrontRegardless(long window);
|
||||
static native void orderWindow(long window, int ordered, long relativeTo);
|
||||
static native void orderWindowIfOnActiveSpace(long window, int ordered, long relativeTo);
|
||||
|
||||
/**
|
||||
* Removes the window from the screen.
|
||||
|
||||
@@ -67,6 +67,9 @@ static AWTWindow* lastKeyWindow = nil;
|
||||
// It would be NSZeroPoint if 'Location by Platform' is not used.
|
||||
static NSPoint lastTopLeftPoint;
|
||||
|
||||
static BOOL fullScreenTransitionInProgress = NO;
|
||||
static BOOL orderingScheduled = NO;
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// NSWindow/NSPanel descendants implementation
|
||||
#define AWT_NS_WINDOW_IMPLEMENTATION \
|
||||
@@ -323,9 +326,11 @@ AWT_NS_WINDOW_IMPLEMENTATION
|
||||
|
||||
if (IS(mask, FULLSCREENABLE) && [self.nsWindow respondsToSelector:@selector(toggleFullScreen:)]) {
|
||||
if (IS(bits, FULLSCREENABLE)) {
|
||||
[self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
|
||||
self.nsWindow.collectionBehavior = self.nsWindow.collectionBehavior |
|
||||
NSWindowCollectionBehaviorFullScreenPrimary;
|
||||
} else {
|
||||
[self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault];
|
||||
self.nsWindow.collectionBehavior = self.nsWindow.collectionBehavior &
|
||||
~NSWindowCollectionBehaviorFullScreenPrimary;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,16 +399,14 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
self.ownerWindow = owner;
|
||||
[self setPropertiesForStyleBits:styleBits mask:MASK(_METHOD_PROP_BITMASK)];
|
||||
|
||||
if (IS(self.styleBits, IS_POPUP)) {
|
||||
[self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
|
||||
}
|
||||
|
||||
if (IS(bits, SHEET) && owner != nil) {
|
||||
[self.nsWindow setStyleMask: NSWindowStyleMaskDocModalWindow];
|
||||
}
|
||||
|
||||
self.isJustCreated = YES;
|
||||
|
||||
self.nsWindow.collectionBehavior = NSWindowCollectionBehaviorManaged;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -533,6 +536,69 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
- (BOOL) delayShowing {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
return ownerWindow != nil &&
|
||||
([ownerWindow delayShowing] || !ownerWindow.nsWindow.onActiveSpace) &&
|
||||
!nsWindow.visible;
|
||||
}
|
||||
|
||||
- (BOOL) checkBlockingAndOrder {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
|
||||
if (platformWindow != NULL) {
|
||||
GET_CPLATFORM_WINDOW_CLASS_RETURN(NO);
|
||||
DECLARE_METHOD_RETURN(jm_checkBlockingAndOrder, jc_CPlatformWindow, "checkBlockingAndOrder", "()V", NO);
|
||||
(*env)->CallVoidMethod(env, platformWindow, jm_checkBlockingAndOrder);
|
||||
CHECK_EXCEPTION();
|
||||
(*env)->DeleteLocalRef(env, platformWindow);
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (void)activeSpaceDidChange {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
if (fullScreenTransitionInProgress) {
|
||||
orderingScheduled = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
// show delayed windows
|
||||
for (NSWindow *window in NSApp.windows) {
|
||||
if ([AWTWindow isJavaPlatformWindowVisible:window] && !window.visible) {
|
||||
AWTWindow *awtWindow = (AWTWindow *)[window delegate];
|
||||
while (awtWindow.ownerWindow != nil) {
|
||||
awtWindow = awtWindow.ownerWindow;
|
||||
}
|
||||
if (awtWindow.nsWindow.visible && awtWindow.nsWindow.onActiveSpace) {
|
||||
[awtWindow checkBlockingAndOrder];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) processVisibleChildren:(void(^)(AWTWindow*))action {
|
||||
NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
|
||||
NSWindow *window;
|
||||
while ((window = [windowEnumerator nextObject]) != nil) {
|
||||
if ([AWTWindow isJavaPlatformWindowVisible:window]) {
|
||||
AWTWindow *awtWindow = (AWTWindow *)[window delegate];
|
||||
AWTWindow *parent = awtWindow.ownerWindow;
|
||||
while (parent != nil) {
|
||||
if (parent == self) {
|
||||
action(awtWindow);
|
||||
break;
|
||||
}
|
||||
parent = parent.ownerWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Orders window children based on the current focus state
|
||||
- (void) orderChildWindows:(BOOL)focus {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
@@ -542,37 +608,23 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
return;
|
||||
}
|
||||
|
||||
NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
|
||||
NSWindow *window;
|
||||
while ((window = [windowEnumerator nextObject]) != nil) {
|
||||
if ([AWTWindow isJavaPlatformWindowVisible:window]) {
|
||||
AWTWindow *awtWindow = (AWTWindow *)[window delegate];
|
||||
AWTWindow *owner = awtWindow.ownerWindow;
|
||||
if (IS(awtWindow.styleBits, ALWAYS_ON_TOP)) {
|
||||
// Do not order 'always on top' windows
|
||||
continue;
|
||||
}
|
||||
while (awtWindow.ownerWindow != nil) {
|
||||
if (awtWindow.ownerWindow == self) {
|
||||
if (focus) {
|
||||
// Move the childWindow to floating level
|
||||
// so it will appear in front of its
|
||||
// parent which owns the focus
|
||||
[window setLevel:NSFloatingWindowLevel];
|
||||
} else {
|
||||
// Focus owner has changed, move the childWindow
|
||||
// back to normal window level
|
||||
[window setLevel:NSNormalWindowLevel];
|
||||
}
|
||||
// The childWindow should be displayed in front of
|
||||
// its nearest parentWindow
|
||||
[window orderWindow:NSWindowAbove relativeTo:[owner.nsWindow windowNumber]];
|
||||
break;
|
||||
}
|
||||
awtWindow = awtWindow.ownerWindow;
|
||||
[self processVisibleChildren:^void(AWTWindow* child){
|
||||
// Do not order 'always on top' windows
|
||||
if (!IS(child.styleBits, ALWAYS_ON_TOP)) {
|
||||
NSWindow *window = child.nsWindow;
|
||||
NSWindow *owner = child.ownerWindow.nsWindow;
|
||||
if (focus) {
|
||||
// Move the childWindow to floating level
|
||||
// so it will appear in front of its
|
||||
// parent which owns the focus
|
||||
[window setLevel:NSFloatingWindowLevel];
|
||||
} else {
|
||||
// Focus owner has changed, move the childWindow
|
||||
// back to normal window level
|
||||
[window setLevel:NSNormalWindowLevel];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// NSWindow overrides
|
||||
@@ -601,15 +653,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
// We should bring up the modal dialog manually
|
||||
[AWTToolkit eventCountPlusPlus];
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
|
||||
if (platformWindow != NULL) {
|
||||
GET_CPLATFORM_WINDOW_CLASS_RETURN(NO);
|
||||
DECLARE_METHOD_RETURN(jm_checkBlockingAndOrder, jc_CPlatformWindow, "checkBlockingAndOrder", "()Z", NO);
|
||||
(*env)->CallBooleanMethod(env, platformWindow, jm_checkBlockingAndOrder);
|
||||
CHECK_EXCEPTION();
|
||||
(*env)->DeleteLocalRef(env, platformWindow);
|
||||
}
|
||||
if (![self checkBlockingAndOrder]) return NO;
|
||||
}
|
||||
|
||||
return self.isEnabled && IS(self.styleBits, SHOULD_BECOME_MAIN);
|
||||
@@ -684,24 +728,14 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
- (void) iconifyChildWindows:(BOOL)iconify {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
|
||||
NSWindow *window;
|
||||
while ((window = [windowEnumerator nextObject]) != nil) {
|
||||
if ([AWTWindow isJavaPlatformWindowVisible:window]) {
|
||||
AWTWindow *awtWindow = (AWTWindow *)[window delegate];
|
||||
while (awtWindow.ownerWindow != nil) {
|
||||
if (awtWindow.ownerWindow == self) {
|
||||
if (iconify) {
|
||||
[window orderOut:window];
|
||||
} else {
|
||||
[window orderFront:window];
|
||||
}
|
||||
break;
|
||||
}
|
||||
awtWindow = awtWindow.ownerWindow;
|
||||
}
|
||||
[self processVisibleChildren:^void(AWTWindow* child){
|
||||
NSWindow *window = child.nsWindow;
|
||||
if (iconify) {
|
||||
[window orderOut:window];
|
||||
} else {
|
||||
[window orderFront:window];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) _deliverIconify:(BOOL)iconify {
|
||||
@@ -934,8 +968,33 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
// this is required to move owned windows to the full-screen space when owner goes to full-screen mode
|
||||
- (void)allowMovingChildrenBetweenSpaces:(BOOL)allow {
|
||||
[self processVisibleChildren:^void(AWTWindow* child){
|
||||
NSWindow *window = child.nsWindow;
|
||||
NSWindowCollectionBehavior behavior = window.collectionBehavior;
|
||||
behavior &= ~(NSWindowCollectionBehaviorManaged | NSWindowCollectionBehaviorTransient);
|
||||
behavior |= allow ? NSWindowCollectionBehaviorTransient : NSWindowCollectionBehaviorManaged;
|
||||
window.collectionBehavior = behavior;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) fullScreenTransitionStarted {
|
||||
fullScreenTransitionInProgress = YES;
|
||||
}
|
||||
|
||||
- (void) fullScreenTransitionFinished {
|
||||
fullScreenTransitionInProgress = NO;
|
||||
if (orderingScheduled) {
|
||||
orderingScheduled = NO;
|
||||
[self checkBlockingAndOrder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillEnterFullScreen:(NSNotification *)notification {
|
||||
[self fullScreenTransitionStarted];
|
||||
[self allowMovingChildrenBetweenSpaces:YES];
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
GET_CPLATFORM_WINDOW_CLASS();
|
||||
DECLARE_METHOD(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
|
||||
@@ -949,6 +1008,9 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
|
||||
[self allowMovingChildrenBetweenSpaces:NO];
|
||||
[self fullScreenTransitionFinished];
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
GET_CPLATFORM_WINDOW_CLASS();
|
||||
DECLARE_METHOD(jm_windowDidEnterFullScreen, jc_CPlatformWindow, "windowDidEnterFullScreen", "()V");
|
||||
@@ -963,6 +1025,8 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification *)notification {
|
||||
[self fullScreenTransitionStarted];
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
GET_CPLATFORM_WINDOW_CLASS();
|
||||
DECLARE_METHOD(jm_windowWillExitFullScreen, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
|
||||
@@ -981,6 +1045,8 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)notification {
|
||||
[self fullScreenTransitionFinished];
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
|
||||
if (platformWindow != NULL) {
|
||||
@@ -1815,3 +1881,26 @@ JNI_COCOA_ENTER(env);
|
||||
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CPlatformWindow
|
||||
* Method: nativeDelayShowing
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeDelayShowing
|
||||
(JNIEnv *env, jclass clazz, jlong windowPtr)
|
||||
{
|
||||
__block jboolean result = JNI_FALSE;
|
||||
|
||||
JNI_COCOA_ENTER(env);
|
||||
|
||||
NSWindow *nsWindow = (NSWindow *)jlong_to_ptr(windowPtr);
|
||||
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
|
||||
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
|
||||
result = [window delayShowing];
|
||||
}];
|
||||
|
||||
JNI_COCOA_EXIT(env);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#import "ThreadUtilities.h"
|
||||
#import "NSApplicationAWT.h"
|
||||
#import "JNIUtilities.h"
|
||||
#import "AWTWindow.h"
|
||||
|
||||
#pragma mark App Menu helpers
|
||||
|
||||
@@ -268,6 +269,11 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
[ctr addObserver:clz selector:@selector(_appDidUnhide) name:NSApplicationDidUnhideNotification object:nil];
|
||||
[ctr addObserver:clz selector:@selector(_didChangeScreenParameters) name:NSApplicationDidChangeScreenParametersNotification object:nil];
|
||||
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:[AWTWindow class]
|
||||
selector:@selector(activeSpaceDidChange)
|
||||
name:NSWorkspaceActiveSpaceDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,25 @@ JNI_COCOA_ENTER(env);
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CWrapper$NSWindow
|
||||
* Method: orderFrontIfOnActiveSpace
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderFrontIfOnActiveSpace
|
||||
(JNIEnv *env, jclass cls, jlong windowPtr)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
|
||||
NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
|
||||
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
|
||||
if (window.onActiveSpace) [window orderFront:window];
|
||||
}];
|
||||
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CWrapper$NSWindow
|
||||
* Method: nativeOrderOut
|
||||
@@ -231,6 +250,26 @@ JNI_COCOA_ENTER(env);
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CWrapper$NSWindow
|
||||
* Method: orderWindowIfOnActiveSpace
|
||||
* Signature: (JIJ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderWindowIfOnActiveSpace
|
||||
(JNIEnv *env, jclass cls, jlong windowPtr, jint order, jlong relativeToPtr)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
|
||||
NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
|
||||
NSWindow *relativeTo = (NSWindow *)jlong_to_ptr(relativeToPtr);
|
||||
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
|
||||
if (window.onActiveSpace) [window orderWindow:(NSWindowOrderingMode)order relativeTo:[relativeTo windowNumber]];
|
||||
}];
|
||||
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
// Used for CWrapper.NSWindow.setLevel() (and level() which isn't implemented yet)
|
||||
static NSInteger LEVELS[sun_lwawt_macosx_CWrapper_NSWindow_MAX_WINDOW_LEVELS];
|
||||
static void initLevels()
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2000-2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3671 Window order changes for a background app on macOS desktop space switch
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @compile MacSpacesUtil.java
|
||||
* @run main BackgroundWindowOrderOnSpaceChange
|
||||
*/
|
||||
|
||||
public class BackgroundWindowOrderOnSpaceChange {
|
||||
private static JFrame frame1;
|
||||
private static JFrame frame2;
|
||||
private static Process otherProcess;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MacSpacesUtil.ensureMoreThanOneSpaceExists();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(BackgroundWindowOrderOnSpaceChange::initUI);
|
||||
launchProcessWithWindow();
|
||||
MacSpacesUtil.switchToNextSpace();
|
||||
MacSpacesUtil.switchToPreviousSpace();
|
||||
Color color = new Robot().getPixelColor(400, 400);
|
||||
if (!Color.green.equals(color)) {
|
||||
throw new RuntimeException("Frame 1 isn't shown on top. Found color: " + color);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
SwingUtilities.invokeAndWait(BackgroundWindowOrderOnSpaceChange::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame1 = new JFrame("BackgroundWindowOrderOnSpaceChange 1");
|
||||
frame1.getContentPane().setBackground(Color.green);
|
||||
frame1.setBounds(100, 100, 400, 400);
|
||||
frame1.setVisible(true);
|
||||
frame2 = new JFrame("BackgroundWindowOrderOnSpaceChange 2");
|
||||
frame2.getContentPane().setBackground(Color.red);
|
||||
frame2.setBounds(300, 300, 400, 400);
|
||||
frame2.setVisible(true);
|
||||
frame1.toFront();
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame1 != null) frame1.dispose();
|
||||
if (frame2 != null) frame2.dispose();
|
||||
if (otherProcess != null) otherProcess.destroyForcibly();
|
||||
}
|
||||
|
||||
private static void launchProcessWithWindow() throws Exception {
|
||||
String javaPath = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
|
||||
File tmpFile = File.createTempFile("BackgroundWindowOrderOnSpaceChange", ".java");
|
||||
tmpFile.deleteOnExit();
|
||||
Files.writeString(tmpFile.toPath(), "import javax.swing.*;\n" +
|
||||
"import java.awt.event.*;\n" +
|
||||
"\n" +
|
||||
"public class TestWindow {\n" +
|
||||
" public static void main(String[] args) {\n" +
|
||||
" SwingUtilities.invokeLater(() -> {\n" +
|
||||
" JFrame f = new JFrame(\"BackgroundWindowOrderOnSpaceChange 3\");\n" +
|
||||
" f.addWindowFocusListener(new WindowAdapter() {\n" +
|
||||
" @Override\n" +
|
||||
" public void windowGainedFocus(WindowEvent e) {\n" +
|
||||
" System.out.println();\n" +
|
||||
" }\n" +
|
||||
" });\n" +
|
||||
" f.setBounds(800, 100, 200, 200);\n" +
|
||||
" f.setVisible(true);\n" +
|
||||
" });\n" +
|
||||
" }\n" +
|
||||
"}\n");
|
||||
otherProcess = Runtime.getRuntime().exec(new String[]{javaPath, tmpFile.getAbsolutePath()});
|
||||
if (otherProcess.getInputStream().read() == -1) {
|
||||
throw new RuntimeException("Error starting process");
|
||||
}
|
||||
}
|
||||
}
|
||||
123
test/jdk/jb/java/awt/Window/FullScreenChildWindow.java
Normal file
123
test/jdk/jb/java/awt/Window/FullScreenChildWindow.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import com.apple.eawt.Application;
|
||||
import com.apple.eawt.FullScreenAdapter;
|
||||
import com.apple.eawt.FullScreenUtilities;
|
||||
import com.apple.eawt.event.FullScreenEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3611 Unexpected workspace switch with dialog in full-screen mode on macOS
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/com.apple.eawt
|
||||
* java.desktop/com.apple.eawt.event
|
||||
* @compile MacSpacesUtil.java
|
||||
* @run main FullScreenChildWindow
|
||||
*/
|
||||
|
||||
public class FullScreenChildWindow {
|
||||
private static final CompletableFuture<Boolean> shownAtFullScreen = new CompletableFuture<>();
|
||||
private static final CompletableFuture<Boolean> dialogShown = new CompletableFuture<>();
|
||||
|
||||
private static Robot robot;
|
||||
private static JFrame frame2;
|
||||
private static JFrame frame1;
|
||||
private static JButton button;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(FullScreenChildWindow::initUI);
|
||||
shownAtFullScreen.get(5, TimeUnit.SECONDS);
|
||||
clickAt(button);
|
||||
dialogShown.get(5, TimeUnit.SECONDS);
|
||||
MacSpacesUtil.switchToPreviousSpace();
|
||||
if (!frame1.isFocused()) {
|
||||
throw new RuntimeException("Unexpected state");
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(FullScreenChildWindow::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame1 = new JFrame("FullScreenChildWindow(1)");
|
||||
frame1.setSize(100, 100);
|
||||
frame1.setLocation(100, 100);
|
||||
frame1.setVisible(true);
|
||||
|
||||
frame2 = new JFrame("FullScreenChildWindow(2)");
|
||||
button = new JButton("Open dialog");
|
||||
button.addActionListener(e -> {
|
||||
JDialog d = new JDialog(frame2, "dialog", false);
|
||||
d.setSize(100, 100);
|
||||
d.setLocationRelativeTo(null);
|
||||
d.setAutoRequestFocus(false);
|
||||
d.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
dialogShown.complete(true);
|
||||
}
|
||||
});
|
||||
d.setVisible(true);
|
||||
});
|
||||
frame2.add(button);
|
||||
frame2.setSize(100, 100);
|
||||
frame2.setLocation(100, 300);
|
||||
frame2.setVisible(true);
|
||||
FullScreenUtilities.addFullScreenListenerTo(frame2, new FullScreenAdapter() {
|
||||
@Override
|
||||
public void windowEnteredFullScreen(FullScreenEvent e) {
|
||||
shownAtFullScreen.complete(true);
|
||||
}
|
||||
});
|
||||
Application.getApplication().requestToggleFullScreen(frame2);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame2 != null) frame2.dispose();
|
||||
if (frame1 != null) frame1.dispose();
|
||||
}
|
||||
|
||||
private static void clickAt(int x, int y) {
|
||||
robot.mouseMove(x, y);
|
||||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||
}
|
||||
|
||||
private static void clickAt(Component component) {
|
||||
Point location = component.getLocationOnScreen();
|
||||
clickAt(location.x + component.getWidth() / 2, location.y + component.getHeight() / 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import com.apple.eawt.Application;
|
||||
|
||||
import java.awt.Window;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3666 Child window stays on default space
|
||||
* when full-screen mode is activated for parent window on macOS
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/com.apple.eawt
|
||||
* @compile MacSpacesUtil.java
|
||||
* @run main FullScreenChildWindowShownBefore
|
||||
*/
|
||||
|
||||
public class FullScreenChildWindowShownBefore {
|
||||
private static JFrame frame;
|
||||
private static JDialog dialog;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(FullScreenChildWindowShownBefore::initUI);
|
||||
Thread.sleep(1000); // wait for windows to appear
|
||||
SwingUtilities.invokeAndWait(() -> Application.getApplication().requestToggleFullScreen(frame));
|
||||
Thread.sleep(1000); // wait for transition to full screen to finish
|
||||
ensureVisible(frame, 250, 150);
|
||||
ensureVisible(dialog, 250, 250);
|
||||
SwingUtilities.invokeAndWait(() -> Application.getApplication().requestToggleFullScreen(frame));
|
||||
Thread.sleep(1000); // wait for transition from full screen to finish
|
||||
ensureVisible(frame, 250, 150);
|
||||
ensureVisible(dialog, 250, 250);
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(FullScreenChildWindowShownBefore::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame = new JFrame("FullScreenChildWindowShownBefore");
|
||||
frame.setBounds(100, 100, 300, 300);
|
||||
frame.setVisible(true);
|
||||
|
||||
dialog = new JDialog(frame, false);
|
||||
dialog.setBounds(200, 200, 100, 100);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame != null) frame.dispose();
|
||||
}
|
||||
|
||||
private static void ensureVisible(Window window, int x, int y) throws Exception {
|
||||
if (!MacSpacesUtil.isWindowVisibleAtPoint(window, x, y)) throw new RuntimeException(window + " isn't visible");
|
||||
}
|
||||
}
|
||||
100
test/jdk/jb/java/awt/Window/FullScreenInactiveDialog.java
Normal file
100
test/jdk/jb/java/awt/Window/FullScreenInactiveDialog.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import com.apple.eawt.Application;
|
||||
import com.apple.eawt.FullScreenAdapter;
|
||||
import com.apple.eawt.FullScreenUtilities;
|
||||
import com.apple.eawt.event.FullScreenEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3633 Modal dialog is shown not at the same space as its parent
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/com.apple.eawt
|
||||
* java.desktop/com.apple.eawt.event
|
||||
* @compile MacSpacesUtil.java
|
||||
* @run main FullScreenInactiveDialog
|
||||
*/
|
||||
|
||||
public class FullScreenInactiveDialog {
|
||||
private static final CompletableFuture<Boolean> shownAtFullScreen = new CompletableFuture<>();
|
||||
|
||||
private static JFrame frame;
|
||||
private static JDialog dialog;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(FullScreenInactiveDialog::initUI);
|
||||
shownAtFullScreen.get(5, TimeUnit.SECONDS);
|
||||
MacSpacesUtil.switchToPreviousSpace();
|
||||
SwingUtilities.invokeLater(FullScreenInactiveDialog::openDialog);
|
||||
Thread.sleep(1000);
|
||||
if (MacSpacesUtil.isWindowVisible(dialog)) {
|
||||
throw new RuntimeException("Dialog is showing earlier than expected");
|
||||
}
|
||||
Desktop.getDesktop().requestForeground(false); // simulates clicking on app icon in dock or switch using Cmd+Tab
|
||||
Thread.sleep(1000); // wait for animation to finish
|
||||
if (!MacSpacesUtil.isWindowVisible(dialog)) {
|
||||
throw new RuntimeException("Dialog isn't showing when expected");
|
||||
}
|
||||
if (!MacSpacesUtil.isWindowVisible(frame)) {
|
||||
throw new RuntimeException("Frame isn't showing when expected");
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(FullScreenInactiveDialog::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame = new JFrame("FullScreenInactiveDialog");
|
||||
frame.setSize(300, 100);
|
||||
frame.setVisible(true);
|
||||
|
||||
|
||||
FullScreenUtilities.addFullScreenListenerTo(frame, new FullScreenAdapter() {
|
||||
@Override
|
||||
public void windowEnteredFullScreen(FullScreenEvent e) {
|
||||
shownAtFullScreen.complete(true);
|
||||
}
|
||||
});
|
||||
Application.getApplication().requestToggleFullScreen(frame);
|
||||
}
|
||||
|
||||
private static void openDialog() {
|
||||
dialog = new JDialog(frame);
|
||||
dialog.setSize(100, 100);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame != null) frame.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2000-2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import com.apple.eawt.Application;
|
||||
import com.apple.eawt.FullScreenAdapter;
|
||||
import com.apple.eawt.FullScreenUtilities;
|
||||
import com.apple.eawt.event.FullScreenEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3633 Modal dialog is shown not at the same space as its parent
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/com.apple.eawt
|
||||
* java.desktop/com.apple.eawt.event
|
||||
* @compile MacSpacesUtil.java
|
||||
* @run main FullScreenInactiveModalDialog
|
||||
*/
|
||||
|
||||
public class FullScreenInactiveModalDialog {
|
||||
private static final CompletableFuture<Boolean> shownAtFullScreen = new CompletableFuture<>();
|
||||
|
||||
private static JFrame frame;
|
||||
private static JDialog dialog;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(FullScreenInactiveModalDialog::initUI);
|
||||
shownAtFullScreen.get(5, TimeUnit.SECONDS);
|
||||
MacSpacesUtil.switchToPreviousSpace();
|
||||
SwingUtilities.invokeLater(FullScreenInactiveModalDialog::openDialog);
|
||||
Thread.sleep(1000);
|
||||
if (MacSpacesUtil.isWindowVisible(dialog)) {
|
||||
throw new RuntimeException("Dialog is showing earlier than expected");
|
||||
}
|
||||
MacSpacesUtil.switchToNextSpace();
|
||||
if (!MacSpacesUtil.isWindowVisible(dialog)) {
|
||||
throw new RuntimeException("Dialog isn't showing when expected");
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(FullScreenInactiveModalDialog::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame = new JFrame("FullScreenInactiveModalDialog");
|
||||
frame.setSize(300, 100);
|
||||
frame.setVisible(true);
|
||||
|
||||
|
||||
FullScreenUtilities.addFullScreenListenerTo(frame, new FullScreenAdapter() {
|
||||
@Override
|
||||
public void windowEnteredFullScreen(FullScreenEvent e) {
|
||||
shownAtFullScreen.complete(true);
|
||||
}
|
||||
});
|
||||
Application.getApplication().requestToggleFullScreen(frame);
|
||||
}
|
||||
|
||||
private static void openDialog() {
|
||||
dialog = new JDialog(frame, true);
|
||||
dialog.setSize(100, 100);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame != null) frame.dispose();
|
||||
}
|
||||
}
|
||||
170
test/jdk/jb/java/awt/Window/MacSpacesUtil.java
Normal file
170
test/jdk/jb/java/awt/Window/MacSpacesUtil.java
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class MacSpacesUtil {
|
||||
public static boolean hasMoreThanOneSpace() throws Exception {
|
||||
return Runtime.getRuntime().exec(new String[]{
|
||||
"plutil",
|
||||
"-extract",
|
||||
"SpacesDisplayConfiguration.Management Data.Monitors.0.Spaces.1",
|
||||
"json",
|
||||
"-o",
|
||||
"-",
|
||||
System.getProperty("user.home") + "/Library/Preferences/com.apple.spaces.plist"
|
||||
}).waitFor() == 0;
|
||||
}
|
||||
|
||||
public static void addSpace() throws Exception {
|
||||
toggleMissionControl();
|
||||
|
||||
// press button at top right corner to add a new space
|
||||
Rectangle screenBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
|
||||
.getDefaultConfiguration().getBounds();
|
||||
int rightX = screenBounds.x + screenBounds.width;
|
||||
int topY = screenBounds.y;
|
||||
Robot robot = new Robot();
|
||||
robot.setAutoDelay(50);
|
||||
robot.mouseMove(rightX, topY);
|
||||
robot.mouseMove(rightX - 5, topY + 5);
|
||||
robot.mouseMove(rightX - 10, topY + 10);
|
||||
robot.delay(1000);
|
||||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.delay(1000);
|
||||
|
||||
toggleMissionControl();
|
||||
}
|
||||
|
||||
public static boolean isWindowVisibleAtPoint(Window window, int x, int y) throws Exception {
|
||||
CountDownLatch movementDetected = new CountDownLatch(1);
|
||||
MouseMotionListener listener = new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
movementDetected.countDown();
|
||||
}
|
||||
};
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> window.addMouseMotionListener(listener));
|
||||
Robot robot = new Robot();
|
||||
robot.mouseMove(x, y);
|
||||
robot.delay(50);
|
||||
robot.mouseMove(x + 1, y + 1);
|
||||
return movementDetected.await(1, TimeUnit.SECONDS);
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(() -> window.removeMouseMotionListener(listener));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWindowVisible(Window window) throws Exception {
|
||||
Rectangle bounds = window.getBounds();
|
||||
Insets insets = window.getInsets();
|
||||
bounds.x += insets.left;
|
||||
bounds.y += insets.top;
|
||||
bounds.width -= insets.left + insets.right;
|
||||
bounds.height -= insets.top + insets.bottom;
|
||||
return isWindowVisibleAtPoint(window, bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
|
||||
}
|
||||
|
||||
public static void verifyAdditionalSpaceExists() throws Exception {
|
||||
AtomicReference<JFrame> frameRef = new AtomicReference<>();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
JFrame f = new JFrame("Spaces check");
|
||||
frameRef.set(f);
|
||||
f.setBounds(100, 100, 200, 200);
|
||||
f.setVisible(true);
|
||||
Desktop.getDesktop().requestForeground(true);
|
||||
});
|
||||
|
||||
if (!isWindowVisibleAtPoint(frameRef.get(), 200, 200)) {
|
||||
throw new RuntimeException("Test frame not visible");
|
||||
}
|
||||
|
||||
switchToNextSpace();
|
||||
|
||||
if (isWindowVisibleAtPoint(frameRef.get(), 200, 200)) {
|
||||
throw new RuntimeException("Extra space isn't available");
|
||||
}
|
||||
} finally {
|
||||
switchToPreviousSpace();
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
JFrame f = frameRef.get();
|
||||
if (f != null) f.dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void toggleMissionControl() throws Exception {
|
||||
Robot robot = new Robot();
|
||||
robot.setAutoDelay(50);
|
||||
robot.keyPress(KeyEvent.VK_CONTROL);
|
||||
robot.keyPress(KeyEvent.VK_UP);
|
||||
robot.keyRelease(KeyEvent.VK_UP);
|
||||
robot.keyRelease(KeyEvent.VK_CONTROL);
|
||||
robot.delay(1000); // wait for animation to finish
|
||||
}
|
||||
|
||||
public static void switchToPreviousSpace() throws Exception {
|
||||
Robot robot = new Robot();
|
||||
robot.setAutoDelay(50);
|
||||
robot.keyPress(KeyEvent.VK_CONTROL);
|
||||
robot.keyPress(KeyEvent.VK_LEFT);
|
||||
robot.keyRelease(KeyEvent.VK_LEFT);
|
||||
robot.keyRelease(KeyEvent.VK_CONTROL);
|
||||
robot.delay(1000); // wait for animation to finish
|
||||
}
|
||||
|
||||
public static void switchToNextSpace() throws Exception {
|
||||
Robot robot = new Robot();
|
||||
robot.setAutoDelay(50);
|
||||
robot.keyPress(KeyEvent.VK_CONTROL);
|
||||
robot.keyPress(KeyEvent.VK_RIGHT);
|
||||
robot.keyRelease(KeyEvent.VK_RIGHT);
|
||||
robot.keyRelease(KeyEvent.VK_CONTROL);
|
||||
robot.delay(1000); // wait for animation to finish
|
||||
}
|
||||
|
||||
// press Control+Right while holding mouse pressed over window's header
|
||||
public static void moveWindowToNextSpace(Window window) throws Exception {
|
||||
Robot robot = new Robot();
|
||||
robot.setAutoDelay(50);
|
||||
Point location = window.getLocationOnScreen();
|
||||
robot.mouseMove(location.x + window.getWidth() / 2, location.y + window.getInsets().top / 2);
|
||||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||
switchToNextSpace();
|
||||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||
}
|
||||
|
||||
public static void ensureMoreThanOneSpaceExists() throws Exception {
|
||||
if (hasMoreThanOneSpace()) return;
|
||||
addSpace();
|
||||
verifyAdditionalSpaceExists();
|
||||
}
|
||||
}
|
||||
81
test/jdk/jb/java/awt/Window/ParentMovingToAnotherSpace.java
Normal file
81
test/jdk/jb/java/awt/Window/ParentMovingToAnotherSpace.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-4186 Unexpected desktop switch after moving a window to another desktop
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @compile MacSpacesUtil.java
|
||||
* @run main ParentMovingToAnotherSpace
|
||||
*/
|
||||
|
||||
public class ParentMovingToAnotherSpace {
|
||||
private static JFrame frame1;
|
||||
private static JFrame frame2;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MacSpacesUtil.ensureMoreThanOneSpaceExists();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(ParentMovingToAnotherSpace::initUI);
|
||||
Thread.sleep(1000); // wait for windows to appear
|
||||
MacSpacesUtil.moveWindowToNextSpace(frame2);
|
||||
if (MacSpacesUtil.isWindowVisible(frame1)) {
|
||||
throw new RuntimeException("Space switch didn't work");
|
||||
}
|
||||
if (!MacSpacesUtil.isWindowVisible(frame2)) {
|
||||
throw new RuntimeException("Frame isn't showing when expected");
|
||||
}
|
||||
} finally {
|
||||
MacSpacesUtil.switchToPreviousSpace();
|
||||
SwingUtilities.invokeAndWait(ParentMovingToAnotherSpace::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame1 = new JFrame("ParentMovingToAnotherSpace-1");
|
||||
frame1.setBounds(100, 100, 300, 100);
|
||||
frame1.setVisible(true);
|
||||
|
||||
frame2 = new JFrame("ParentMovingToAnotherSpace-2");
|
||||
frame2.setBounds(100, 300, 300, 100);
|
||||
frame2.setVisible(true);
|
||||
|
||||
JWindow window = new JWindow(frame2);
|
||||
window.setBounds(100, 500, 300, 100);
|
||||
window.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame1 != null) frame1.dispose();
|
||||
if (frame2 != null) frame2.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2000-2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Specific case for JBR-4186 Unexpected desktop switch after moving a window to another desktop
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @compile MacSpacesUtil.java
|
||||
* @run main TwoFramesAndDialogOnDifferentSpaces
|
||||
*/
|
||||
|
||||
public class TwoFramesAndDialogOnDifferentSpaces {
|
||||
private static Robot robot;
|
||||
private static JFrame frame1;
|
||||
private static JFrame frame2;
|
||||
private static JDialog dialog;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MacSpacesUtil.ensureMoreThanOneSpaceExists();
|
||||
robot = new Robot();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(TwoFramesAndDialogOnDifferentSpaces::initUI);
|
||||
robot.delay(1000);
|
||||
ensureVisibility(true, true, true);
|
||||
MacSpacesUtil.moveWindowToNextSpace(frame1);
|
||||
ensureVisibility(true, false, false);
|
||||
MacSpacesUtil.switchToPreviousSpace();
|
||||
ensureVisibility(false, true, true);
|
||||
clickOn(frame2);
|
||||
robot.delay(1000);
|
||||
MacSpacesUtil.switchToNextSpace();
|
||||
ensureVisibility(true, false, false);
|
||||
} finally {
|
||||
MacSpacesUtil.switchToPreviousSpace();
|
||||
SwingUtilities.invokeAndWait(TwoFramesAndDialogOnDifferentSpaces::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame1 = new JFrame("TFADODS-1");
|
||||
frame1.setBounds(200, 200, 300, 100);
|
||||
frame1.setVisible(true);
|
||||
|
||||
frame2 = new JFrame("TFADODS-2");
|
||||
frame2.setBounds(200, 400, 300, 100);
|
||||
frame2.setVisible(true);
|
||||
|
||||
dialog = new JDialog(frame2, "TFADODS");
|
||||
dialog.setBounds(200, 600, 300, 100);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame1 != null) frame1.dispose();
|
||||
if (frame2 != null) frame2.dispose();
|
||||
}
|
||||
|
||||
private static void ensureVisibility(boolean frame1Visible, boolean frame2Visible, boolean dialogVisible)
|
||||
throws Exception {
|
||||
if (frame1Visible != MacSpacesUtil.isWindowVisible(frame1)) {
|
||||
throw new RuntimeException("Frame 1 is " + (frame1Visible ? "not " : "") + " visible");
|
||||
}
|
||||
if (frame2Visible != MacSpacesUtil.isWindowVisible(frame2)) {
|
||||
throw new RuntimeException("Frame 2 is " + (frame2Visible ? "not " : "") + " visible");
|
||||
}
|
||||
if (dialogVisible != MacSpacesUtil.isWindowVisible(dialog)) {
|
||||
throw new RuntimeException("Dialog is " + (dialogVisible ? "not " : "") + " visible");
|
||||
}
|
||||
}
|
||||
|
||||
private static void clickAt(int x, int y) {
|
||||
robot.mouseMove(x, y);
|
||||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||
}
|
||||
|
||||
private static void clickOn(Component component) {
|
||||
Point location = component.getLocationOnScreen();
|
||||
clickAt(location.x + component.getWidth() / 2, location.y + component.getHeight() / 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user