JBR-3676 WINDOW_ACTIVATED/DEACTIVATED events sent to a frame when child window closes on macOS

(cherry-picked from commit 824f9ebec3)
This commit is contained in:
Dmitry Batrak
2021-08-10 12:13:19 +03:00
committed by jbrbot
parent eff56a9db9
commit b70aa1cd09
4 changed files with 178 additions and 60 deletions

View File

@@ -302,23 +302,6 @@ 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.
LWWindowPeer owner = getOwnerFrameDialog(LWWindowPeer.this);
if (owner != null) {
owner.requestWindowFocus(FocusEvent.Cause.ACTIVATION);
}
}
}
}
@Override

View File

@@ -88,6 +88,7 @@ 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);
@@ -751,7 +752,9 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
// Actually show or hide the window
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
if (visible && delayShowing()) {
if (!visible) {
execute(ptr -> AWTThreading.executeWaitToolkit(wait -> nativeHideWindow(ptr, wait)));
} else if (delayShowing()) {
if (blocker == null) {
Window focusedWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
LWWindowPeer focusedWindowBlocker = getBlockerFor(focusedWindow);
@@ -761,51 +764,42 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
owner.execute(CWrapper.NSWindow::orderFront);
}
}
} else if (blocker == null || !visible) {
} else if (blocker == null) {
// If it ain't blocked, or is being hidden, go regular way
if (visible) {
contentView.execute(viewPtr -> {
execute(ptr -> CWrapper.NSWindow.makeFirstResponder(ptr,
viewPtr));
});
contentView.execute(viewPtr -> {
execute(ptr -> CWrapper.NSWindow.makeFirstResponder(ptr,
viewPtr));
});
boolean isPopup = (target.getType() == Window.Type.POPUP);
execute(ptr -> {
if (isPopup) {
// Popups in applets don't activate applet's process
CWrapper.NSWindow.orderFrontRegardless(ptr);
} else {
CWrapper.NSWindow.orderFront(ptr);
boolean isPopup = (target.getType() == Window.Type.POPUP);
execute(ptr -> {
if (isPopup) {
// Popups in applets don't activate applet's process
CWrapper.NSWindow.orderFrontRegardless(ptr);
} else {
CWrapper.NSWindow.orderFront(ptr);
}
boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr);
if (!isKeyWindow) {
logger.fine("setVisible: makeKeyWindow");
CWrapper.NSWindow.makeKeyWindow(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: makeKeyWindow");
CWrapper.NSWindow.makeKeyWindow(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

View File

@@ -1552,6 +1552,38 @@ JNI_COCOA_ENTER(env);
JNI_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)
{
JNI_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];
}];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeSetNSWindowTitle

View File

@@ -0,0 +1,109 @@
/*
* 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);
}
}