mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-31 13:49:55 +01:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dec27719cf | ||
|
|
c4fb2e5e55 |
@@ -934,7 +934,7 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
|
||||
|
||||
int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight(
|
||||
getTarget(), lightweightChild, temporary,
|
||||
focusedWindowChangeAllowed, time, cause, true);
|
||||
focusedWindowChangeAllowed, time, cause, false);
|
||||
switch (result) {
|
||||
case LWKeyboardFocusManagerPeer.SNFH_FAILURE:
|
||||
return false;
|
||||
@@ -968,22 +968,15 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean res = parentPeer.requestWindowFocus(cause);
|
||||
// If parent window can be made focused and has been made focused (synchronously)
|
||||
// then we can proceed with children, otherwise we retreat
|
||||
if (!res || !parentWindow.isFocused()) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine("request rejected, res= " + res + ", parentWindow.isFocused()=" +
|
||||
parentWindow.isFocused());
|
||||
}
|
||||
return parentPeer.requestWindowFocus(cause, () -> {
|
||||
LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
|
||||
return false;
|
||||
}
|
||||
}, () -> {
|
||||
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
|
||||
|
||||
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
|
||||
Component focusOwner = kfmPeer.getCurrentFocusOwner();
|
||||
return LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
|
||||
getTarget(), true, cause, focusOwner);
|
||||
Component focusOwner = kfmPeer.getCurrentFocusOwner();
|
||||
LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
|
||||
getTarget(), false, cause, focusOwner);
|
||||
});
|
||||
|
||||
case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED:
|
||||
return true;
|
||||
|
||||
@@ -61,18 +61,18 @@ public class LWLightweightFramePeer extends LWWindowPeer implements OverrideNati
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestWindowFocus(FocusEvent.Cause cause) {
|
||||
public boolean requestWindowFocus(FocusEvent.Cause cause, Runnable r, Runnable lightweightRequest) {
|
||||
if (!focusAllowedFor()) {
|
||||
return false;
|
||||
}
|
||||
if (getPlatformWindow().rejectFocusRequest(cause)) {
|
||||
/*if (getPlatformWindow().rejectFocusRequest(cause)) {
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
|
||||
Window opposite = LWKeyboardFocusManagerPeer.getInstance().
|
||||
getCurrentFocusedWindow();
|
||||
|
||||
changeFocusedWindow(true, opposite);
|
||||
changeFocusedWindow(true, opposite, () -> {});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -284,6 +284,33 @@ public class LWWindowPeer
|
||||
super.setVisibleImpl(visible);
|
||||
// TODO: update graphicsConfig, see 4868278
|
||||
platformWindow.setVisible(visible);
|
||||
if (isSimpleWindow()) {
|
||||
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
|
||||
if (visible) {
|
||||
if (!getTarget().isAutoRequestFocus()) {
|
||||
return;
|
||||
} else {
|
||||
requestWindowFocus(FocusEvent.Cause.ACTIVATION, () -> {}, () -> {});
|
||||
}
|
||||
// Focus the owner in case this window is focused.
|
||||
} else if (kfmPeer.getCurrentFocusedWindow() == getTarget()) {
|
||||
// Transfer focus to the owner.
|
||||
Window targetOwner = LWWindowPeer.this.getTarget().getOwner();
|
||||
|
||||
while (targetOwner != null && (targetOwner.getOwner() != null && !targetOwner.isFocusableWindow())) {
|
||||
targetOwner = targetOwner.getOwner();
|
||||
}
|
||||
|
||||
if (targetOwner != null) {
|
||||
|
||||
LWWindowPeer owner = (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(targetOwner);
|
||||
|
||||
if (owner != null) {
|
||||
owner.requestWindowFocus(FocusEvent.Cause.ACTIVATION, () -> {}, () -> {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -758,7 +785,7 @@ public class LWWindowPeer
|
||||
@Override
|
||||
public void notifyActivation(boolean activation, LWWindowPeer opposite) {
|
||||
Window oppositeWindow = (opposite == null)? null : opposite.getTarget();
|
||||
changeFocusedWindow(activation, oppositeWindow);
|
||||
changeFocusedWindow(activation, oppositeWindow, () -> {});
|
||||
}
|
||||
|
||||
// MouseDown in non-client area
|
||||
@@ -875,7 +902,7 @@ public class LWWindowPeer
|
||||
// 2. An active but not focused owner frame/dialog is clicked.
|
||||
// The mouse event then will trigger a focus request "in window" to the component, so the window
|
||||
// should gain focus before.
|
||||
requestWindowFocus(FocusEvent.Cause.MOUSE_EVENT);
|
||||
requestWindowFocus(FocusEvent.Cause.MOUSE_EVENT, () -> {}, () -> {});
|
||||
|
||||
mouseDownTarget[targetIdx] = targetPeer;
|
||||
} else if (id == MouseEvent.MOUSE_DRAGGED) {
|
||||
@@ -1231,17 +1258,19 @@ public class LWWindowPeer
|
||||
* Requests platform to set native focus on a frame/dialog.
|
||||
* In case of a simple window, triggers appropriate java focus change.
|
||||
*/
|
||||
public boolean requestWindowFocus(FocusEvent.Cause cause) {
|
||||
public boolean requestWindowFocus(FocusEvent.Cause cause, Runnable rejectFocusRequest, Runnable lightweightRequest) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine("requesting native focus to " + this);
|
||||
}
|
||||
|
||||
if (!focusAllowedFor()) {
|
||||
focusLog.fine("focus is not allowed");
|
||||
rejectFocusRequest.run();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (platformWindow.rejectFocusRequest(cause)) {
|
||||
rejectFocusRequest.run();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1256,13 +1285,19 @@ public class LWWindowPeer
|
||||
|
||||
// In case the toplevel is active but not focused, change focus directly,
|
||||
// as requesting native focus on it will not have effect.
|
||||
if (getTarget() == currentActive && !getTarget().hasFocus()) {
|
||||
|
||||
changeFocusedWindow(true, opposite);
|
||||
if (getTarget() == currentActive && !getTarget().isFocused()) {
|
||||
changeFocusedWindow(true, opposite, lightweightRequest);
|
||||
return true;
|
||||
}
|
||||
|
||||
return platformWindow.requestWindowFocus();
|
||||
focusLog.fine("platformWindow.requestWindowFocus()");
|
||||
boolean requestFocusResult = platformWindow.requestWindowFocus();
|
||||
|
||||
if (requestFocusResult) {
|
||||
lightweightRequest.run();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean focusAllowedFor() {
|
||||
@@ -1290,7 +1325,7 @@ public class LWWindowPeer
|
||||
|
||||
@Override
|
||||
public void emulateActivation(boolean activate) {
|
||||
changeFocusedWindow(activate, null);
|
||||
changeFocusedWindow(activate, null, () -> {});
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@@ -1309,7 +1344,7 @@ public class LWWindowPeer
|
||||
/*
|
||||
* Changes focused window on java level.
|
||||
*/
|
||||
protected void changeFocusedWindow(boolean becomesFocused, Window opposite) {
|
||||
protected void changeFocusedWindow(boolean becomesFocused, Window opposite, Runnable lightweightRequestRunnable) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine((becomesFocused?"gaining":"loosing") + " focus window: " + this);
|
||||
}
|
||||
@@ -1348,19 +1383,22 @@ public class LWWindowPeer
|
||||
}
|
||||
|
||||
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
|
||||
|
||||
if (!becomesFocused && kfmPeer.getCurrentFocusedWindow() != getTarget()) {
|
||||
// late window focus lost event - ingoring
|
||||
return;
|
||||
}
|
||||
|
||||
kfmPeer.setCurrentFocusedWindow(becomesFocused ? getTarget() : null);
|
||||
|
||||
int eventID = becomesFocused ? WindowEvent.WINDOW_GAINED_FOCUS : WindowEvent.WINDOW_LOST_FOCUS;
|
||||
WindowEvent windowEvent = new TimedWindowEvent(getTarget(), eventID, opposite, System.currentTimeMillis());
|
||||
|
||||
SunToolkit.setSystemGenerated(windowEvent);
|
||||
AWTAccessor.getAWTEventAccessor().setPosted(windowEvent);
|
||||
PeerEvent pe = new PeerEvent(getTarget(), () -> {
|
||||
((Component)windowEvent.getSource()).dispatchEvent(windowEvent);
|
||||
if (becomesFocused) {
|
||||
lightweightRequestRunnable.run();
|
||||
}
|
||||
}, PeerEvent.ULTIMATE_PRIORITY_EVENT);
|
||||
|
||||
// TODO: wrap in SequencedEvent
|
||||
SunToolkit.postPriorityEvent(windowEvent);
|
||||
postEvent(pe);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1481,4 +1519,4 @@ public class LWWindowPeer
|
||||
}
|
||||
return handle[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ class CAccessibility implements PropertyChangeListener {
|
||||
static <T> T invokeAndWait(final Callable<T> callable, final Component c, final T defValue) {
|
||||
T value = null;
|
||||
try {
|
||||
value = LWCToolkit.invokeAndWait(callable, c, INVOKE_TIMEOUT_SECONDS);
|
||||
value = EventQueue.isDispatchThread() ? callable.call() : LWCToolkit.invokeAndWait(callable, c, INVOKE_TIMEOUT_SECONDS);
|
||||
} catch (final Exception e) { e.printStackTrace(); }
|
||||
|
||||
return value != null ? value : defValue;
|
||||
@@ -603,132 +603,87 @@ class CAccessibility implements PropertyChangeListener {
|
||||
if (a == null) return null;
|
||||
return invokeAndWait(new Callable<Object[]>() {
|
||||
public Object[] call() throws Exception {
|
||||
return getChildrenAndRolesImpl(a, c, whichChildren, allowIgnored);
|
||||
ArrayList<Object> childrenAndRoles = new ArrayList<Object>();
|
||||
_addChildren(a, whichChildren, allowIgnored, childrenAndRoles);
|
||||
|
||||
/* In the case of fetching a selection, need to check to see if
|
||||
* the active descendant is at the beginning of the list. If it
|
||||
* is not it needs to be moved to the beginning of the list so
|
||||
* VoiceOver will annouce it correctly. The list returned
|
||||
* from Java is always in order from top to bottom, but when shift
|
||||
* selecting downward (extending the list) or multi-selecting using
|
||||
* the VO keys control+option+command+return the active descendant
|
||||
* is not at the top of the list in the shift select down case and
|
||||
* may not be in the multi select case.
|
||||
*/
|
||||
if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
|
||||
if (!childrenAndRoles.isEmpty()) {
|
||||
AccessibleContext activeDescendantAC =
|
||||
CAccessible.getActiveDescendant(a);
|
||||
if (activeDescendantAC != null) {
|
||||
String activeDescendantName =
|
||||
activeDescendantAC.getAccessibleName();
|
||||
AccessibleRole activeDescendantRole =
|
||||
activeDescendantAC.getAccessibleRole();
|
||||
// Move active descendant to front of list.
|
||||
// List contains pairs of each selected item's
|
||||
// Accessible and AccessibleRole.
|
||||
ArrayList<Object> newArray = new ArrayList<Object>();
|
||||
int count = childrenAndRoles.size();
|
||||
Accessible currentAccessible = null;
|
||||
AccessibleContext currentAC = null;
|
||||
String currentName = null;
|
||||
AccessibleRole currentRole = null;
|
||||
for (int i = 0; i < count; i+=2) {
|
||||
// Is this the active descendant?
|
||||
currentAccessible = (Accessible)childrenAndRoles.get(i);
|
||||
currentAC = currentAccessible.getAccessibleContext();
|
||||
currentName = currentAC.getAccessibleName();
|
||||
currentRole = (AccessibleRole)childrenAndRoles.get(i+1);
|
||||
if ( currentName.equals(activeDescendantName) &&
|
||||
currentRole.equals(activeDescendantRole) ) {
|
||||
newArray.add(0, currentAccessible);
|
||||
newArray.add(1, currentRole);
|
||||
} else {
|
||||
newArray.add(currentAccessible);
|
||||
newArray.add(currentRole);
|
||||
}
|
||||
}
|
||||
childrenAndRoles = newArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) {
|
||||
return childrenAndRoles.toArray();
|
||||
}
|
||||
|
||||
return new Object[] { childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1) };
|
||||
}
|
||||
}, c);
|
||||
}
|
||||
|
||||
private static Object[] getChildrenAndRolesImpl(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) {
|
||||
if (a == null) return null;
|
||||
|
||||
ArrayList<Object> childrenAndRoles = new ArrayList<Object>();
|
||||
_addChildren(a, whichChildren, allowIgnored, childrenAndRoles);
|
||||
|
||||
/* In case of fetching a selection, we need to check if
|
||||
* the active descendant is at the beginning of the list, or
|
||||
* otherwise move it, so that VoiceOver announces it correctly.
|
||||
* The java list is always in order from top to bottom, but when
|
||||
* (1) shift-selecting downward (extending the list) or (2) multi-selecting with
|
||||
* the VO keys (CTRL+ALT+CMD+RETURN) the active descendant
|
||||
* is not at the top of the list in the 1st case and may not be in the 2nd.
|
||||
*/
|
||||
if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
|
||||
if (!childrenAndRoles.isEmpty()) {
|
||||
AccessibleContext activeDescendantAC =
|
||||
CAccessible.getActiveDescendant(a);
|
||||
if (activeDescendantAC != null) {
|
||||
String activeDescendantName =
|
||||
activeDescendantAC.getAccessibleName();
|
||||
AccessibleRole activeDescendantRole =
|
||||
activeDescendantAC.getAccessibleRole();
|
||||
// Move active descendant to front of list.
|
||||
// List contains pairs of each selected item's
|
||||
// Accessible and AccessibleRole.
|
||||
ArrayList<Object> newArray = new ArrayList<Object>();
|
||||
int count = childrenAndRoles.size();
|
||||
Accessible currentAccessible = null;
|
||||
AccessibleContext currentAC = null;
|
||||
String currentName = null;
|
||||
AccessibleRole currentRole = null;
|
||||
for (int i = 0; i < count; i += 2) {
|
||||
// Is this the active descendant?
|
||||
currentAccessible = (Accessible) childrenAndRoles.get(i);
|
||||
currentAC = currentAccessible.getAccessibleContext();
|
||||
currentName = currentAC.getAccessibleName();
|
||||
currentRole = (AccessibleRole) childrenAndRoles.get(i + 1);
|
||||
if (currentName != null && currentName.equals(activeDescendantName) &&
|
||||
currentRole.equals(activeDescendantRole)) {
|
||||
newArray.add(0, currentAccessible);
|
||||
newArray.add(1, currentRole);
|
||||
} else {
|
||||
newArray.add(currentAccessible);
|
||||
newArray.add(currentRole);
|
||||
}
|
||||
}
|
||||
childrenAndRoles = newArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) {
|
||||
return childrenAndRoles.toArray();
|
||||
}
|
||||
|
||||
return new Object[]{childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1)};
|
||||
}
|
||||
|
||||
// This method is called from the native
|
||||
// Each child takes up three entries in the array: one for itself, one for its role, and one for the recursion level
|
||||
private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored, final int level) {
|
||||
public static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored, final int level) {
|
||||
if (a == null) return null;
|
||||
return invokeAndWait(new Callable<Object[]>() {
|
||||
public Object[] call() throws Exception {
|
||||
ArrayList<Object> currentLevelChildren = new ArrayList<Object>();
|
||||
currentLevelChildren.addAll(Arrays.asList(getChildrenAndRoles(a, c, JAVA_AX_ALL_CHILDREN, allowIgnored)));
|
||||
ArrayList<Object> allChildren = new ArrayList<Object>();
|
||||
ArrayList<Accessible> parentStack = new ArrayList<Accessible>();
|
||||
parentStack.add(a);
|
||||
ArrayList<Integer> indexses = new ArrayList<Integer>();
|
||||
Integer index = 0;
|
||||
int currentLevel = level;
|
||||
while (!parentStack.isEmpty()) {
|
||||
Accessible p = parentStack.get(parentStack.size() - 1);
|
||||
|
||||
currentLevelChildren.addAll(Arrays.asList(getChildrenAndRolesImpl(p, c, JAVA_AX_ALL_CHILDREN, allowIgnored)));
|
||||
if ((currentLevelChildren.size() == 0) || (index >= currentLevelChildren.size())) {
|
||||
if (!parentStack.isEmpty()) parentStack.remove(parentStack.size() - 1);
|
||||
if (!indexses.isEmpty()) index = indexses.remove(indexses.size() - 1);
|
||||
currentLevel -= 1;
|
||||
currentLevelChildren.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessible ca = null;
|
||||
Object obj = currentLevelChildren.get(index);
|
||||
if (!(obj instanceof Accessible)) {
|
||||
index += 2;
|
||||
currentLevelChildren.clear();
|
||||
continue;
|
||||
}
|
||||
ca = (Accessible) obj;
|
||||
Object role = currentLevelChildren.get(index + 1);
|
||||
currentLevelChildren.clear();
|
||||
|
||||
AccessibleContext cac = ca.getAccessibleContext();
|
||||
if (cac == null) {
|
||||
index += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((cac.getAccessibleStateSet().contains(AccessibleState.SELECTED) && (whichChildren == JAVA_AX_SELECTED_CHILDREN)) ||
|
||||
(cac.getAccessibleStateSet().contains(AccessibleState.VISIBLE) && (whichChildren == JAVA_AX_VISIBLE_CHILDREN)) ||
|
||||
for (int i = 0; i < currentLevelChildren.size(); i += 2) {
|
||||
if ((((Accessible) currentLevelChildren.get(i)).getAccessibleContext().getAccessibleStateSet().contains(AccessibleState.SELECTED) && (whichChildren == JAVA_AX_SELECTED_CHILDREN)) ||
|
||||
(((Accessible) currentLevelChildren.get(i)).getAccessibleContext().getAccessibleStateSet().contains(AccessibleState.VISIBLE) && (whichChildren == JAVA_AX_VISIBLE_CHILDREN)) ||
|
||||
(whichChildren == JAVA_AX_ALL_CHILDREN)) {
|
||||
allChildren.add(ca);
|
||||
allChildren.add(role);
|
||||
allChildren.add(String.valueOf(currentLevel));
|
||||
allChildren.add(currentLevelChildren.get(i));
|
||||
allChildren.add(currentLevelChildren.get(i + 1));
|
||||
allChildren.add(String.valueOf(level));
|
||||
}
|
||||
|
||||
index += 2;
|
||||
|
||||
if (cac.getAccessibleStateSet().contains(AccessibleState.EXPANDED)) {
|
||||
parentStack.add(ca);
|
||||
indexses.add(index);
|
||||
index = 0;
|
||||
currentLevel += 1;
|
||||
continue;
|
||||
if (getAccessibleStateSet(((Accessible) currentLevelChildren.get(i)).getAccessibleContext(), c).contains(AccessibleState.EXPANDED)) {
|
||||
allChildren.addAll(Arrays.asList(getChildrenAndRolesRecursive(((Accessible) currentLevelChildren.get(i)), c, whichChildren, allowIgnored, level + 1)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return allChildren.toArray();
|
||||
}
|
||||
}, c);
|
||||
|
||||
@@ -96,7 +96,6 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
private static native void nativeSetNSWindowMinMax(long nsWindowPtr, double minW, double minH, double maxW, double maxH);
|
||||
private static native void nativePushNSWindowToBack(long nsWindowPtr);
|
||||
private static native void nativePushNSWindowToFront(long nsWindowPtr, boolean wait);
|
||||
private static native void nativeHideWindow(long nsWindowPtr, boolean wait);
|
||||
private static native void nativeSetNSWindowTitle(long nsWindowPtr, String title);
|
||||
private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr);
|
||||
private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage);
|
||||
@@ -761,9 +760,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
|
||||
// Actually show or hide the window
|
||||
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
|
||||
if (!visible) {
|
||||
execute(ptr -> AWTThreading.executeWaitToolkit(wait -> nativeHideWindow(ptr, wait)));
|
||||
} else if (delayShowing()) {
|
||||
if (visible && delayShowing()) {
|
||||
if (blocker == null) {
|
||||
Window focusedWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
|
||||
LWWindowPeer focusedWindowBlocker = getBlockerFor(focusedWindow);
|
||||
@@ -773,34 +770,43 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
owner.execute(CWrapper.NSWindow::orderFront);
|
||||
}
|
||||
}
|
||||
} else if (blocker == null) {
|
||||
} else if (blocker == null || !visible) {
|
||||
// If it ain't blocked, or is being hidden, go regular way
|
||||
boolean isPopup = (target.getType() == Window.Type.POPUP);
|
||||
execute(ptr -> {
|
||||
if (visible) {
|
||||
boolean isPopup = (target.getType() == Window.Type.POPUP);
|
||||
execute(ptr -> {
|
||||
|
||||
boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr);
|
||||
if (!isKeyWindow) {
|
||||
logger.fine("setVisible: makeKeyAndOrderFront");
|
||||
CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
|
||||
} else {
|
||||
logger.fine("setVisible: orderFront");
|
||||
CWrapper.NSWindow.orderFront(ptr);
|
||||
}
|
||||
|
||||
if (owner != null
|
||||
&& owner.getPeer() instanceof LWLightweightFramePeer) {
|
||||
LWLightweightFramePeer peer =
|
||||
(LWLightweightFramePeer) owner.getPeer();
|
||||
|
||||
long ownerWindowPtr = peer.getOverriddenWindowHandle();
|
||||
if (ownerWindowPtr != 0) {
|
||||
//Place window above JavaFX stage
|
||||
CWrapper.NSWindow.addChildWindow(
|
||||
ownerWindowPtr, ptr,
|
||||
CWrapper.NSWindow.NSWindowAbove);
|
||||
boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr);
|
||||
if (!isKeyWindow) {
|
||||
logger.fine("setVisible: makeKeyAndOrderFront");
|
||||
CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
|
||||
} else {
|
||||
logger.fine("setVisible: orderFront");
|
||||
CWrapper.NSWindow.orderFront(ptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (owner != null
|
||||
&& owner.getPeer() instanceof LWLightweightFramePeer) {
|
||||
LWLightweightFramePeer peer =
|
||||
(LWLightweightFramePeer) owner.getPeer();
|
||||
|
||||
long ownerWindowPtr = peer.getOverriddenWindowHandle();
|
||||
if (ownerWindowPtr != 0) {
|
||||
//Place window above JavaFX stage
|
||||
CWrapper.NSWindow.addChildWindow(
|
||||
ownerWindowPtr, ptr,
|
||||
CWrapper.NSWindow.NSWindowAbove);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
execute(ptr->{
|
||||
// immediately hide the window
|
||||
CWrapper.NSWindow.orderOut(ptr);
|
||||
// process the close
|
||||
CWrapper.NSWindow.close(ptr);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// otherwise, put it in a proper z-order
|
||||
CPlatformWindow bw
|
||||
|
||||
@@ -622,7 +622,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
for (NSWindow* window in [NSApp windows]) {
|
||||
if (window.onActiveSpace && window.mainWindow && [AWTWindow isJavaPlatformWindowVisible:window]) {
|
||||
if (window.onActiveSpace && [AWTWindow isJavaPlatformWindowVisible:window]) {
|
||||
AWTWindow *awtWindow = (AWTWindow *)[window delegate];
|
||||
// there can be only one current blocker per window hierarchy,
|
||||
// so we're checking just hierarchy root
|
||||
@@ -635,24 +635,6 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
- (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's childs based on the current focus state
|
||||
- (void) orderChildWindows:(BOOL)focus {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
@@ -662,28 +644,39 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
return;
|
||||
}
|
||||
|
||||
[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];
|
||||
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;
|
||||
}
|
||||
if (window.onActiveSpace && owner.onActiveSpace) {
|
||||
// The childWindow should be displayed in front of
|
||||
// its nearest parentWindow
|
||||
[window orderWindow:NSWindowAbove relativeTo:[owner windowNumber]];
|
||||
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];
|
||||
}
|
||||
if (window.onActiveSpace && owner.nsWindow.onActiveSpace) {
|
||||
// The childWindow should be displayed in front of
|
||||
// its nearest parentWindow
|
||||
[window orderWindow:NSWindowAbove relativeTo:[owner.nsWindow windowNumber]];
|
||||
}
|
||||
break;
|
||||
}
|
||||
awtWindow = awtWindow.ownerWindow;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// NSWindow overrides
|
||||
@@ -790,14 +783,24 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
- (void) iconifyChildWindows:(BOOL)iconify {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
[self processVisibleChildren:^void(AWTWindow* child){
|
||||
NSWindow *window = child.nsWindow;
|
||||
if (iconify) {
|
||||
[window orderOut:window];
|
||||
} else {
|
||||
[window orderFront:window];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _deliverIconify:(BOOL)iconify {
|
||||
@@ -1000,22 +1003,10 @@ 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)windowWillEnterFullScreen:(NSNotification *)notification {
|
||||
self.isEnterFullScreen = YES;
|
||||
|
||||
[self allowMovingChildrenBetweenSpaces:YES];
|
||||
|
||||
|
||||
static JNF_MEMBER_CACHE(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
|
||||
@@ -1028,9 +1019,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
|
||||
self.isEnterFullScreen = YES;
|
||||
|
||||
[self allowMovingChildrenBetweenSpaces:NO];
|
||||
|
||||
|
||||
static JNF_MEMBER_CACHE(jm_windowDidEnterFullScreen, jc_CPlatformWindow, "windowDidEnterFullScreen", "()V");
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
|
||||
@@ -1514,38 +1503,6 @@ JNF_COCOA_ENTER(env);
|
||||
JNF_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CPlatformWindow
|
||||
* Method: nativeHideWindow
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeHideWindow
|
||||
(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean wait)
|
||||
{
|
||||
JNF_COCOA_ENTER(env);
|
||||
|
||||
NSWindow *nsWindow = OBJC(windowPtr);
|
||||
[ThreadUtilities performOnMainThreadWaiting:(BOOL)wait block:^(){
|
||||
if (nsWindow.keyWindow) {
|
||||
// When 'windowDidResignKey' is called during 'orderOut', current key window
|
||||
// is reported as 'nil', so it's impossible to create WINDOW_FOCUS_LOST event
|
||||
// with correct 'opposite' window.
|
||||
// So, as a workaround, we perform focus transfer to a parent window explicitly here.
|
||||
NSWindow *parentWindow = nsWindow;
|
||||
while ((parentWindow = ((AWTWindow*)parentWindow.delegate).ownerWindow.nsWindow) != nil) {
|
||||
if (parentWindow.canBecomeKeyWindow) {
|
||||
[parentWindow makeKeyWindow];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
[nsWindow orderOut:nsWindow];
|
||||
[nsWindow close];
|
||||
}];
|
||||
|
||||
JNF_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CPlatformWindow
|
||||
* Method: nativeSetNSWindowTitle
|
||||
@@ -1746,19 +1703,6 @@ JNF_COCOA_ENTER(env);
|
||||
|
||||
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
|
||||
if ((([nsWindow styleMask] & NSFullScreenWindowMask) != NSFullScreenWindowMask)) {
|
||||
if (!NSApp.active) {
|
||||
// use undocumented approach to avoid focus stealing
|
||||
NSKeyedArchiver *coder = [[NSKeyedArchiver alloc] init];
|
||||
[nsWindow encodeRestorableStateWithCoder:coder];
|
||||
[coder encodeBool:YES forKey:@"NSIsFullScreen"];
|
||||
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:coder.encodedData];
|
||||
[nsWindow restoreStateWithCoder:decoder];
|
||||
[decoder finishDecoding];
|
||||
[decoder release];
|
||||
[coder release];
|
||||
if (nsWindow.styleMask & NSWindowStyleMaskFullScreen) return; // success
|
||||
// otherwise fall back to standard approach
|
||||
}
|
||||
[nsWindow performSelector:toggleFullScreenSelector withObject:nil];
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* 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 javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3686 Background window steals focus when converted to full screen on macOS
|
||||
* @key headful
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/com.apple.eawt
|
||||
*/
|
||||
|
||||
public class FullScreenFocusStealing {
|
||||
private static JFrame frame;
|
||||
private static Process otherProcess;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(FullScreenFocusStealing::initUI);
|
||||
launchProcessWithWindow();
|
||||
toggleFullScreen();
|
||||
Thread.sleep(1000);
|
||||
assertProcessWindowIsStillFocused();
|
||||
}
|
||||
finally {
|
||||
SwingUtilities.invokeAndWait(FullScreenFocusStealing::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame = new JFrame("FullScreenFocusStealing");
|
||||
frame.setBounds(100, 100, 200, 200);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame != null) frame.dispose();
|
||||
if (otherProcess != null) otherProcess.destroyForcibly();
|
||||
}
|
||||
|
||||
private static void toggleFullScreen() throws Exception {
|
||||
Application.class.getDeclaredMethod("requestEnterFullScreen", Window.class)
|
||||
.invoke(Application.getApplication(), frame);
|
||||
}
|
||||
|
||||
private static void assertProcessWindowIsStillFocused() throws Exception {
|
||||
otherProcess.getOutputStream().write('\n');
|
||||
otherProcess.getOutputStream().flush();
|
||||
if (otherProcess.getInputStream().read() != '1') {
|
||||
throw new RuntimeException("Process window lost focus");
|
||||
}
|
||||
}
|
||||
|
||||
private static void launchProcessWithWindow() throws Exception {
|
||||
String javaPath = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
|
||||
File tmpFile = File.createTempFile("FullScreenFocusStealing", ".java");
|
||||
tmpFile.deleteOnExit();
|
||||
Files.writeString(tmpFile.toPath(), "import javax.swing.*;\n" +
|
||||
"import java.awt.event.*;\n" +
|
||||
"\n" +
|
||||
"public class TestWindow {\n" +
|
||||
" private static JFrame frame;\n" +
|
||||
" public static void main(String[] args) throws Exception {\n" +
|
||||
" SwingUtilities.invokeLater(() -> {\n" +
|
||||
" frame = new JFrame(\"FullScreenFocusStealing 2\");\n" +
|
||||
" frame.addWindowFocusListener(new WindowAdapter() {\n" +
|
||||
" @Override\n" +
|
||||
" public void windowGainedFocus(WindowEvent e) {\n" +
|
||||
" System.out.println();\n" +
|
||||
" }\n" +
|
||||
" });\n" +
|
||||
" frame.setBounds(100, 400, 200, 200);\n" +
|
||||
" frame.setVisible(true);\n" +
|
||||
" });\n" +
|
||||
" System.in.read();\n" +
|
||||
" System.out.println(frame.isFocused() ? '1' : '0');\n" +
|
||||
" }\n" +
|
||||
"}\n");
|
||||
otherProcess = Runtime.getRuntime().exec(new String[]{javaPath, tmpFile.getAbsolutePath()});
|
||||
if (otherProcess.getInputStream().read() == -1) {
|
||||
throw new RuntimeException("Error starting process");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* 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 javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3662 Focus jumps to another project tab after closing modal dialog
|
||||
* @key headful
|
||||
*/
|
||||
|
||||
public class WrongFrameFocusedOnModalDialogClosing {
|
||||
private static CompletableFuture<Boolean> result;
|
||||
|
||||
private static Robot robot;
|
||||
private static JFrame frame1;
|
||||
private static JFrame frame2;
|
||||
private static JDialog dialog;
|
||||
private static JButton button;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(WrongFrameFocusedOnModalDialogClosing::initUI);
|
||||
robot.delay(1000); // wait for frames to appear
|
||||
clickOn(button);
|
||||
robot.delay(1000); // wait for dialog to appear
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
result = new CompletableFuture<>();
|
||||
dialog.dispose();
|
||||
});
|
||||
if (result.get(5, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Wrong frame focused");
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(WrongFrameFocusedOnModalDialogClosing::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame1 = new JFrame("WFFOMDC 1");
|
||||
frame1.addWindowFocusListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
if (result != null) result.complete(false);
|
||||
}
|
||||
});
|
||||
dialog = new JDialog(frame1, true);
|
||||
button = new JButton("Open dialog");
|
||||
button.addActionListener(e -> dialog.setVisible(true));
|
||||
frame1.add(button);
|
||||
frame1.setSize(100, 100);
|
||||
frame1.setLocation(100, 100);
|
||||
frame1.setVisible(true);
|
||||
|
||||
frame2 = new JFrame("WFFOMDC 2");
|
||||
frame2.addWindowFocusListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
if (result != null) result.complete(true);
|
||||
}
|
||||
});
|
||||
frame2.setSize(100, 100);
|
||||
frame2.setLocation(300, 100);
|
||||
frame2.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame1 != null) frame1.dispose();
|
||||
if (frame2 != null) frame2.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 clickOn(Component component) {
|
||||
Point location = component.getLocationOnScreen();
|
||||
clickAt(location.x + component.getWidth() / 2, location.y + component.getHeight() / 2);
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* 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")
|
||||
*/
|
||||
|
||||
public class BackgroundWindowOrderOnSpaceChange {
|
||||
private static Robot robot;
|
||||
private static JFrame frame1;
|
||||
private static JFrame frame2;
|
||||
private static Process otherProcess;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(BackgroundWindowOrderOnSpaceChange::initUI);
|
||||
launchProcessWithWindow();
|
||||
switchToNextSpace();
|
||||
switchToPreviousSpace();
|
||||
Color color = 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");
|
||||
}
|
||||
}
|
||||
|
||||
private static void switchToPreviousSpace() {
|
||||
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
|
||||
}
|
||||
|
||||
private static void switchToNextSpace() {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* 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 javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
public class FullScreenChildWindowShownBefore {
|
||||
private static final CompletableFuture<Boolean> dialogShown = new CompletableFuture<>();
|
||||
|
||||
private static Robot robot;
|
||||
private static JFrame frame;
|
||||
private static JDialog dialog;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(FullScreenChildWindowShownBefore::initUI);
|
||||
dialogShown.get(5, TimeUnit.SECONDS);
|
||||
SwingUtilities.invokeAndWait(() -> Application.getApplication().requestToggleFullScreen(frame));
|
||||
robot.delay(1000); // wait for transition to full screen to finish
|
||||
ensureVisible(frame);
|
||||
ensureVisible(dialog);
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(FullScreenChildWindowShownBefore::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame = new JFrame("FullScreenChildWindowShownBefore");
|
||||
frame.setSize(100, 100);
|
||||
frame.setLocation(100, 100);
|
||||
frame.setVisible(true);
|
||||
|
||||
dialog = new JDialog(frame, false);
|
||||
dialog.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
dialogShown.complete(true);
|
||||
}
|
||||
});
|
||||
dialog.setSize(100, 100);
|
||||
dialog.setLocation(100, 300);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame != null) frame.dispose();
|
||||
}
|
||||
|
||||
private static void ensureVisible(Window window) throws Exception {
|
||||
AtomicReference<Point> location = new AtomicReference<>();
|
||||
AtomicBoolean movementDetected = new AtomicBoolean();
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
if (window.isVisible()) {
|
||||
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;
|
||||
if (!bounds.isEmpty()) {
|
||||
location.set(new Point((int) bounds.getCenterX(), (int) bounds.getCenterY()));
|
||||
window.addMouseMotionListener(new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
movementDetected.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Point target = location.get();
|
||||
if (target != null) {
|
||||
robot.mouseMove(target.x, target.y);
|
||||
robot.delay(100);
|
||||
robot.mouseMove(target.x + 1, target.y + 1);
|
||||
robot.delay(1000);
|
||||
if (movementDetected.get()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException(window + " isn't visible");
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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.InputEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3676 WINDOW_ACTIVATED/DEACTIVATED events
|
||||
* sent to a frame when child window closes on macOS
|
||||
* @key headful
|
||||
*/
|
||||
|
||||
public class WindowEventsOnPopupShowing {
|
||||
private static final AtomicInteger events = new AtomicInteger();
|
||||
private static Robot robot;
|
||||
private static JFrame frame;
|
||||
private static JButton openButton;
|
||||
private static JButton closeButton;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(WindowEventsOnPopupShowing::initUI);
|
||||
robot.delay(1000); // wait for frame to be shown
|
||||
events.set(0);
|
||||
clickAt(openButton);
|
||||
robot.delay(1000); // wait for popup to be shown
|
||||
clickAt(closeButton);
|
||||
robot.delay(1000); // wait for popup to be closed
|
||||
int eventCount = events.get();
|
||||
if (eventCount != 0) {
|
||||
throw new RuntimeException("Unexpected events received: " + eventCount);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
SwingUtilities.invokeAndWait(WindowEventsOnPopupShowing::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
openButton = new JButton("Open popup");
|
||||
closeButton = new JButton("Close");
|
||||
|
||||
frame = new JFrame("WindowEventsOnPopupShowing");
|
||||
frame.add(openButton);
|
||||
frame.pack();
|
||||
|
||||
JWindow popup = new JWindow(frame);
|
||||
popup.add(closeButton);
|
||||
popup.pack();
|
||||
|
||||
openButton.addActionListener(e -> popup.setVisible(true));
|
||||
closeButton.addActionListener(e -> popup.dispose());
|
||||
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowActivated(WindowEvent e) {
|
||||
events.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeactivated(WindowEvent e) {
|
||||
events.incrementAndGet();
|
||||
}
|
||||
});
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (frame != null) frame.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);
|
||||
}
|
||||
}
|
||||
@@ -1275,5 +1275,6 @@ jb/java/jcef/MouseEventTest.java
|
||||
jb/java/jcef/MouseEventAfterHideAndShowBrowserTest.java JBR-2750 linux-all
|
||||
jb/java/awt/event/TouchScreenEvent/TouchScreenEventsTest.java nobug windows-6.1 not supported on Windows 7
|
||||
jb/java/awt/Focus/ChainOfPopupsFocusTest.java JBR-1518 linux-all
|
||||
jb/java/awt/Focus/ModalDialogFromMenuTest.java JBR-2768 macosx-all
|
||||
|
||||
java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java JBR-2842 macosx-10.14,macosx-10.15,macosx-10.16
|
||||
|
||||
@@ -211,6 +211,7 @@ jb/javax/swing/JDialog/JDialog705.java
|
||||
jb/javax/swing/JDialog/JDialog741/JDialog741.html nobug macosx-all,linux-all,windows-all
|
||||
jb/sun/awt/macos/KeyPressAndHoldTest.java nobug macosx-all
|
||||
jb/sun/lwawt/macosx/NSEvent/nsevent422.sh nobug macosx-all
|
||||
jb/java/awt/Focus/ModalDialogFromMenuTest.java JBR-2768 macosx-all
|
||||
|
||||
jb/java/jcef/MouseEventTest.java JBR-2750 linux-all
|
||||
jb/java/jcef/MouseEventAfterHideAndShowBrowserTest.java JBR-2750 linux-all
|
||||
Reference in New Issue
Block a user