mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2026-01-09 18:11:42 +01:00
JBR-2533 Popup is not focused on click when switching from another application on macOS
(cherry picked from commitsd9ff151211,67b174dc8c,72b0add80c,21af1eba85,2f1d317d87,6dd334f9f0,cd863bac0d,010f6fc951,25e087d269, parts of7d5ac56b6c,cd6dd5c3cf,e8bbd8ffdd) with fix for JBR-3640 (java/awt/Modal/ModalFocusTransferTests/FocusTransferDWFAppModalTest.java: window Open button lost focus when it should not) and JBR-3979 (Focus is not transferred to parent window) with fix for JBR-5300 Change source code and test files to use GPL license
This commit is contained in:
@@ -955,18 +955,13 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
|
||||
// however that is the shared code and this particular problem's reproducibility has
|
||||
// platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in
|
||||
// current release. TODO: consider fixing it in the shared code.
|
||||
if (!focusedWindowChangeAllowed) {
|
||||
LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ?
|
||||
LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer;
|
||||
|
||||
if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
|
||||
"decoratedPeer is inactive: " + decoratedPeer);
|
||||
}
|
||||
LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
|
||||
return false;
|
||||
if (!focusedWindowChangeAllowed && !parentPeer.getPlatformWindow().isActive()) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
|
||||
"parentPeer is inactive: " + parentPeer);
|
||||
}
|
||||
LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean res = parentPeer.requestWindowFocus(cause);
|
||||
|
||||
@@ -1272,53 +1272,6 @@ public class LWWindowPeer
|
||||
return false;
|
||||
}
|
||||
|
||||
AppContext targetAppContext = AWTAccessor.getComponentAccessor().getAppContext(getTarget());
|
||||
KeyboardFocusManager kfm = AWTAccessor.getKeyboardFocusManagerAccessor()
|
||||
.getCurrentKeyboardFocusManager(targetAppContext);
|
||||
Window currentActive = kfm.getActiveWindow();
|
||||
|
||||
|
||||
Window opposite = LWKeyboardFocusManagerPeer.getInstance().
|
||||
getCurrentFocusedWindow();
|
||||
|
||||
// Make the owner active window.
|
||||
if (isSimpleWindow()) {
|
||||
LWWindowPeer owner = getOwnerFrameDialog(this);
|
||||
|
||||
// If owner is not natively active, request native
|
||||
// activation on it w/o sending events up to java.
|
||||
if (owner != null && !owner.platformWindow.isActive()) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine("requesting native focus to the owner " + owner);
|
||||
}
|
||||
LWWindowPeer currentActivePeer = currentActive == null ? null :
|
||||
(LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(
|
||||
currentActive);
|
||||
|
||||
// Ensure the opposite is natively active and suppress sending events.
|
||||
if (currentActivePeer != null && currentActivePeer.platformWindow.isActive()) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine("the opposite is " + currentActivePeer);
|
||||
}
|
||||
currentActivePeer.skipNextFocusChange = true;
|
||||
}
|
||||
owner.skipNextFocusChange = true;
|
||||
|
||||
owner.platformWindow.requestWindowFocus();
|
||||
}
|
||||
|
||||
// DKFM will synthesize all the focus/activation events correctly.
|
||||
changeFocusedWindow(true, opposite);
|
||||
return true;
|
||||
|
||||
// In case the toplevel is active but not focused, change focus directly,
|
||||
// as requesting native focus on it will not have effect.
|
||||
} else if (getTarget() == currentActive && !getTarget().hasFocus()) {
|
||||
|
||||
changeFocusedWindow(true, opposite);
|
||||
return true;
|
||||
}
|
||||
|
||||
return platformWindow.requestWindowFocus();
|
||||
}
|
||||
|
||||
@@ -1394,7 +1347,8 @@ public class LWWindowPeer
|
||||
// - when the opposite (gaining focus) window is an owned/owner window.
|
||||
// - for a simple window in any case.
|
||||
if (!becomesFocused &&
|
||||
(isGrabbing() || this.isOneOfOwnersOf(grabbingWindow)))
|
||||
(isGrabbing() || this.isOneOfOwnersOf(grabbingWindow)) &&
|
||||
(opposite == null || getOwnerFrameDialog(AWTAccessor.getComponentAccessor().getPeer(opposite)) != this))
|
||||
{
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
focusLog.fine("ungrabbing on " + grabbingWindow);
|
||||
|
||||
@@ -297,18 +297,6 @@ public class CInputMethod extends InputMethodAdapter {
|
||||
if (component.getInputMethodRequests() == null) {
|
||||
imInstance = null;
|
||||
}
|
||||
|
||||
LWWindowPeer windowPeer = peer.getPlatformWindow().getPeer();
|
||||
if (windowPeer.isSimpleWindow()) {
|
||||
// A simple window gains focus. Cocoa won't dispatch IME events into the simple window, but into its owner.
|
||||
// This IM represents the focused component in the simple window. We will use the owner as IME proxy.
|
||||
// For that, this IM is set for the owner and is dropped for the simple window.
|
||||
Window owner = windowPeer.getTarget().getOwner();
|
||||
assert owner != null && owner.isActive();
|
||||
long ownerPtr = getNativeViewPtr((LWComponentPeer)AWTAccessor.getComponentAccessor().getPeer(owner));
|
||||
nativeNotifyPeer(ownerPtr, this);
|
||||
imInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (peer != null) {
|
||||
|
||||
@@ -99,6 +99,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
private static native void nativeEnterFullScreenMode(long nsWindowPtr);
|
||||
private static native void nativeExitFullScreenMode(long nsWindowPtr);
|
||||
static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
|
||||
private static native void nativeRaiseLevel(long nsWindowPtr, boolean popup, boolean onlyIfParentIsActive);
|
||||
|
||||
// Logger to report issues happened during execution but that do not affect functionality
|
||||
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow");
|
||||
@@ -159,7 +160,6 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
static final int MINIMIZABLE = 1 << 8;
|
||||
|
||||
static final int RESIZABLE = 1 << 9; // both a style bit and prop bit
|
||||
static final int NONACTIVATING = 1 << 24;
|
||||
static final int IS_DIALOG = 1 << 25;
|
||||
static final int IS_MODAL = 1 << 26;
|
||||
static final int IS_POPUP = 1 << 27;
|
||||
@@ -421,10 +421,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
// defaults style bits
|
||||
int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | ZOOMABLE | RESIZABLE | TITLE_VISIBLE;
|
||||
|
||||
if (isNativelyFocusableWindow()) {
|
||||
styleBits = SET(styleBits, SHOULD_BECOME_KEY, true);
|
||||
styleBits = SET(styleBits, SHOULD_BECOME_MAIN, true);
|
||||
}
|
||||
styleBits |= getFocusableStyleBits();
|
||||
|
||||
final boolean isFrame = (target instanceof Frame);
|
||||
final boolean isDialog = (target instanceof Dialog);
|
||||
@@ -460,7 +457,6 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
if (isPopup) {
|
||||
styleBits = SET(styleBits, TEXTURED, false);
|
||||
// Popups in applets don't activate applet's process
|
||||
styleBits = SET(styleBits, NONACTIVATING, true);
|
||||
styleBits = SET(styleBits, IS_POPUP, true);
|
||||
}
|
||||
|
||||
@@ -558,9 +554,13 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
return styleBits;
|
||||
}
|
||||
|
||||
// this is the counter-point to -[CWindow _nativeSetStyleBit:]
|
||||
private void setStyleBits(final int mask, final boolean value) {
|
||||
execute(ptr -> nativeSetNSWindowStyleBits(ptr, mask, value ? mask : 0));
|
||||
setStyleBits(mask, value ? mask : 0);
|
||||
}
|
||||
|
||||
// this is the counter-point to -[CWindow _nativeSetStyleBit:]
|
||||
private void setStyleBits(final int mask, final int value) {
|
||||
execute(ptr -> nativeSetNSWindowStyleBits(ptr, mask, value));
|
||||
}
|
||||
|
||||
private native void _toggleFullScreenMode(final long model);
|
||||
@@ -936,8 +936,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
|
||||
@Override
|
||||
public void updateFocusableWindowState() {
|
||||
final boolean isFocusable = isNativelyFocusableWindow();
|
||||
setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
|
||||
setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, getFocusableStyleBits()); // set both bits at once
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1050,7 +1049,6 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
}
|
||||
|
||||
execute(ptr -> nativeSetEnabled(ptr, !blocked));
|
||||
checkBlockingAndOrder();
|
||||
}
|
||||
|
||||
public final void invalidateShadow() {
|
||||
@@ -1199,16 +1197,13 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Our focus model is synthetic and only non-simple window
|
||||
* may become natively focusable window.
|
||||
*/
|
||||
private boolean isNativelyFocusableWindow() {
|
||||
if (peer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !peer.isSimpleWindow() && target.getFocusableWindowState();
|
||||
// returns a combination of SHOULD_BECOME_KEY/SHOULD_BECOME_MAIN relevant for the current window
|
||||
private int getFocusableStyleBits() {
|
||||
return (peer == null || target == null || !target.isFocusableWindow())
|
||||
? 0
|
||||
: peer.isSimpleWindow()
|
||||
? SHOULD_BECOME_KEY
|
||||
: SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1217,8 +1212,11 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
* circumstances.
|
||||
*/
|
||||
private void updateFocusabilityForAutoRequestFocus(boolean isFocusable) {
|
||||
if (target.isAutoRequestFocus() || !isNativelyFocusableWindow()) return;
|
||||
setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
|
||||
if (target.isAutoRequestFocus()) return;
|
||||
int focusableStyleBits = getFocusableStyleBits();
|
||||
if (focusableStyleBits == 0) return;
|
||||
setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN,
|
||||
isFocusable ? focusableStyleBits : 0); // set both bits at once
|
||||
}
|
||||
|
||||
private boolean checkBlockingAndOrder() {
|
||||
@@ -1338,10 +1336,10 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
|
||||
}
|
||||
|
||||
protected void applyWindowLevel(Window target) {
|
||||
if (target.isAlwaysOnTop() && target.getType() != Window.Type.POPUP) {
|
||||
execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSFloatingWindowLevel));
|
||||
} else if (target.getType() == Window.Type.POPUP) {
|
||||
execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSPopUpMenuWindowLevel));
|
||||
boolean popup = target.getType() == Window.Type.POPUP;
|
||||
boolean alwaysOnTop = target.isAlwaysOnTop();
|
||||
if (popup || alwaysOnTop || owner != null) {
|
||||
execute(ptr -> nativeRaiseLevel(ptr, popup, !popup && !alwaysOnTop));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -281,7 +281,6 @@ AWT_NS_WINDOW_IMPLEMENTATION
|
||||
if (IS(styleBits, UTILITY)) type |= NSWindowStyleMaskUtilityWindow;
|
||||
if (IS(styleBits, HUD)) type |= NSWindowStyleMaskHUDWindow;
|
||||
if (IS(styleBits, SHEET)) type |= NSWindowStyleMaskDocModalWindow;
|
||||
if (IS(styleBits, NONACTIVATING)) type |= NSWindowStyleMaskNonactivatingPanel;
|
||||
|
||||
return type;
|
||||
}
|
||||
@@ -367,7 +366,6 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
if (self == nil) return nil; // no hope
|
||||
|
||||
if (IS(bits, UTILITY) ||
|
||||
IS(bits, NONACTIVATING) ||
|
||||
IS(bits, HUD) ||
|
||||
IS(bits, HIDES_ON_DEACTIVATE) ||
|
||||
IS(bits, SHEET))
|
||||
@@ -777,9 +775,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
NSLog(@"became main: %d %@ %@", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow]);
|
||||
#endif
|
||||
|
||||
if (![self.nsWindow isKeyWindow]) {
|
||||
[self activateWindowMenuBar];
|
||||
}
|
||||
[self activateWindowMenuBar];
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
|
||||
@@ -790,6 +786,8 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
CHECK_EXCEPTION();
|
||||
(*env)->DeleteLocalRef(env, platformWindow);
|
||||
}
|
||||
|
||||
[self orderChildWindows:YES];
|
||||
}
|
||||
|
||||
- (void) windowDidBecomeKey: (NSNotification *) notification {
|
||||
@@ -801,13 +799,26 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
AWTWindow *opposite = [AWTWindow lastKeyWindow];
|
||||
|
||||
if (![self.nsWindow isMainWindow]) {
|
||||
[self activateWindowMenuBar];
|
||||
[self makeRelevantAncestorMain];
|
||||
}
|
||||
|
||||
[AWTWindow setLastKeyWindow:nil];
|
||||
|
||||
[self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
|
||||
[self orderChildWindows:YES];
|
||||
}
|
||||
|
||||
- (void) makeRelevantAncestorMain {
|
||||
NSWindow *nativeWindow;
|
||||
AWTWindow *awtWindow = self;
|
||||
|
||||
do {
|
||||
nativeWindow = awtWindow.nsWindow;
|
||||
if ([nativeWindow canBecomeMainWindow]) {
|
||||
[nativeWindow makeMainWindow];
|
||||
break;
|
||||
}
|
||||
awtWindow = awtWindow.ownerWindow;
|
||||
} while (awtWindow);
|
||||
}
|
||||
|
||||
- (void) activateWindowMenuBar {
|
||||
@@ -865,6 +876,9 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
if (![self.nsWindow isKeyWindow]) {
|
||||
[self deactivateWindow];
|
||||
}
|
||||
|
||||
[self.javaMenuBar deactivate];
|
||||
[self orderChildWindows:NO];
|
||||
}
|
||||
|
||||
- (void) deactivateWindow {
|
||||
@@ -872,7 +886,6 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
#ifdef DEBUG
|
||||
NSLog(@"deactivating window: %@", [self.nsWindow title]);
|
||||
#endif
|
||||
[self.javaMenuBar deactivate];
|
||||
|
||||
// the new key window
|
||||
NSWindow *keyWindow = [NSApp keyWindow];
|
||||
@@ -885,7 +898,6 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
|
||||
[self _deliverWindowFocusEvent:NO oppositeWindow: opposite];
|
||||
[self orderChildWindows:NO];
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender {
|
||||
@@ -1241,7 +1253,7 @@ JNI_COCOA_ENTER(env);
|
||||
|
||||
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
|
||||
|
||||
if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
|
||||
if ([nsWindow isMainWindow]) {
|
||||
[window.javaMenuBar deactivate];
|
||||
}
|
||||
|
||||
@@ -1252,7 +1264,7 @@ JNI_COCOA_ENTER(env);
|
||||
actualMenuBar = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
|
||||
}
|
||||
|
||||
if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
|
||||
if ([nsWindow isMainWindow]) {
|
||||
[CMenuBar activate:actualMenuBar modallyDisabled:NO];
|
||||
}
|
||||
}];
|
||||
@@ -1774,3 +1786,25 @@ JNI_COCOA_ENTER(env);
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeRaiseLevel
|
||||
(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean popup, jboolean onlyIfParentIsActive)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
|
||||
NSWindow *nsWindow = OBJC(windowPtr);
|
||||
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
|
||||
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
|
||||
if (onlyIfParentIsActive) {
|
||||
AWTWindow *parent = window;
|
||||
do {
|
||||
parent = parent.ownerWindow;
|
||||
} while (parent != nil && !parent.nsWindow.isMainWindow);
|
||||
if (parent == nil) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
[nsWindow setLevel: popup ? NSPopUpMenuWindowLevel : NSFloatingWindowLevel];
|
||||
}];
|
||||
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
@@ -1081,7 +1081,13 @@ public class Window extends Container implements Accessible {
|
||||
} else {
|
||||
// fix for 6532736: after this window is shown, its blocker
|
||||
// should be raised to front
|
||||
modalBlocker.toFront_NoClientCode();
|
||||
boolean storedValue = modalBlocker.isAutoRequestFocus();
|
||||
modalBlocker.setAutoRequestFocus(false);
|
||||
try {
|
||||
modalBlocker.toFront_NoClientCode();
|
||||
} finally {
|
||||
modalBlocker.setAutoRequestFocus(storedValue);
|
||||
}
|
||||
}
|
||||
if (this instanceof Frame || this instanceof Dialog) {
|
||||
updateChildFocusableWindowState(this);
|
||||
|
||||
152
test/jdk/jb/java/awt/Focus/PopupIncomingFocusTest.java
Normal file
152
test/jdk/jb/java/awt/Focus/PopupIncomingFocusTest.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2000-2023 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.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-2533 Popup is not focused on click when switching from another application on macOS
|
||||
* @key headful
|
||||
*/
|
||||
|
||||
public class PopupIncomingFocusTest {
|
||||
private static final CompletableFuture<Boolean> windowOpened = new CompletableFuture<>();
|
||||
private static final CompletableFuture<Boolean> popupOpened = new CompletableFuture<>();
|
||||
private static final CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||
private static Robot robot;
|
||||
private static Process otherProcess;
|
||||
private static JFrame frame;
|
||||
private static JButton button;
|
||||
private static JWindow popup;
|
||||
private static JTextField field;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
robot.setAutoWaitForIdle(true);
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(PopupIncomingFocusTest::init);
|
||||
windowOpened.get(10, TimeUnit.SECONDS);
|
||||
launchProcessWithWindow();
|
||||
clickAt(button);
|
||||
popupOpened.get(10, TimeUnit.SECONDS);
|
||||
clickAt(400, 100); // other process' window
|
||||
clickAt(field);
|
||||
pressEnter();
|
||||
result.get(10, TimeUnit.SECONDS);
|
||||
}
|
||||
finally {
|
||||
SwingUtilities.invokeAndWait(PopupIncomingFocusTest::shutdown);
|
||||
}
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
button = new JButton("Open popup");
|
||||
button.addActionListener(e -> {
|
||||
popup.setVisible(true);
|
||||
});
|
||||
|
||||
frame = new JFrame();
|
||||
frame.add(button);
|
||||
frame.setBounds(50, 50, 200, 100);
|
||||
frame.addWindowFocusListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
windowOpened.complete(Boolean.TRUE);
|
||||
}
|
||||
});
|
||||
|
||||
field = new JTextField(10);
|
||||
field.getCaret().setBlinkRate(0); // prevent caret blink timer from keeping event thread running
|
||||
field.addActionListener(e -> result.complete(Boolean.TRUE));
|
||||
|
||||
popup = new JWindow(frame);
|
||||
popup.setType(Window.Type.POPUP);
|
||||
popup.add(field);
|
||||
popup.pack();
|
||||
popup.setLocation(50, 200);
|
||||
popup.addWindowFocusListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
popupOpened.complete(Boolean.TRUE);
|
||||
}
|
||||
});
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void shutdown() {
|
||||
if (frame != null) frame.dispose();
|
||||
if (otherProcess != null) otherProcess.destroyForcibly();
|
||||
}
|
||||
|
||||
private static void clickAt(int x, int y) {
|
||||
robot.delay(1000); // needed for GNOME, to give it some time to update internal state after window showing
|
||||
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);
|
||||
}
|
||||
|
||||
private static void pressEnter() {
|
||||
robot.keyPress(KeyEvent.VK_ENTER);
|
||||
robot.keyRelease(KeyEvent.VK_ENTER);
|
||||
}
|
||||
|
||||
private static void launchProcessWithWindow() throws Exception {
|
||||
String javaPath = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
|
||||
File tmpFile = File.createTempFile("PopupIncomingFocusTest", ".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();\n" +
|
||||
" f.addWindowFocusListener(new WindowAdapter() {\n" +
|
||||
" @Override\n" +
|
||||
" public void windowGainedFocus(WindowEvent e) {\n" +
|
||||
" System.out.println();\n" +
|
||||
" }\n" +
|
||||
" });\n" +
|
||||
" f.setBounds(300, 50, 200, 100);\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");
|
||||
}
|
||||
}
|
||||
}
|
||||
97
test/jdk/jb/java/awt/Focus/RequestFocusInParent.java
Normal file
97
test/jdk/jb/java/awt/Focus/RequestFocusInParent.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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-3979 Focus is not transferred to parent window
|
||||
* @key headful
|
||||
*/
|
||||
|
||||
public class RequestFocusInParent {
|
||||
private static CompletableFuture<Boolean> result;
|
||||
|
||||
private static Robot robot;
|
||||
private static JFrame frame;
|
||||
private static JButton button1;
|
||||
private static JButton button2;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(RequestFocusInParent::initUI);
|
||||
robot.delay(1000); // wait for frame to appear
|
||||
clickOn(button1);
|
||||
robot.delay(1000); // wait for popup to appear
|
||||
SwingUtilities.invokeAndWait(() -> result = new CompletableFuture<>());
|
||||
clickOn(button2);
|
||||
result.get(5, TimeUnit.SECONDS);
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(RequestFocusInParent::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame = new JFrame("RequestFocusInParent");
|
||||
button1 = new JButton("Open popup");
|
||||
button1.addActionListener(e -> {
|
||||
JWindow popup = new JWindow(frame);
|
||||
button2 = new JButton("Return focus");
|
||||
button2.addActionListener(ee -> button1.requestFocus());
|
||||
popup.add(button2);
|
||||
popup.setSize(100, 100);
|
||||
popup.setLocation(100, 400);
|
||||
popup.setVisible(true);
|
||||
});
|
||||
button1.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
if (result != null) result.complete(true);
|
||||
}
|
||||
});
|
||||
frame.add(button1);
|
||||
frame.setSize(100, 100);
|
||||
frame.setLocation(100, 100);
|
||||
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 clickOn(Component component) {
|
||||
Point location = component.getLocationOnScreen();
|
||||
clickAt(location.x + component.getWidth() / 2, location.y + component.getHeight() / 2);
|
||||
}
|
||||
}
|
||||
113
test/jdk/jb/java/awt/Focus/WindowWithoutParentTest.java
Normal file
113
test/jdk/jb/java/awt/Focus/WindowWithoutParentTest.java
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2000-2023 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 javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-2854 [macOS] Undecorated window without parent steals focus on showing
|
||||
* @key headful
|
||||
*/
|
||||
|
||||
public class WindowWithoutParentTest {
|
||||
private static final CompletableFuture<Boolean> initFinished = new CompletableFuture<>();
|
||||
private static final CompletableFuture<Boolean> typedInField = new CompletableFuture<>();
|
||||
private static Robot robot;
|
||||
private static JFrame frame;
|
||||
private static JTextField field;
|
||||
private static JWindow window;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
robot.setAutoDelay(50); // ensure different timestamps for key events (can impact typeahead logic)
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(WindowWithoutParentTest::initUI);
|
||||
initFinished.get(10, TimeUnit.SECONDS);
|
||||
clickOn(field);
|
||||
pressAndRelease(KeyEvent.VK_ENTER);
|
||||
pressAndRelease(KeyEvent.VK_A);
|
||||
typedInField.get(10, TimeUnit.SECONDS);
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(WindowWithoutParentTest::disposeUI);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
frame = new JFrame("WindowWithoutParentTest");
|
||||
field = new JTextField(20);
|
||||
field.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
initFinished.complete(true);
|
||||
}
|
||||
});
|
||||
field.addActionListener(e -> {
|
||||
window = new JWindow();
|
||||
window.setSize(50, 50);
|
||||
window.setVisible(true);
|
||||
});
|
||||
field.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
typedInField.complete(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {}
|
||||
});
|
||||
frame.add(field);
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void disposeUI() {
|
||||
if (window != null) window.dispose();
|
||||
if (frame != null) frame.dispose();
|
||||
}
|
||||
|
||||
private static void pressAndRelease(int keyCode) {
|
||||
robot.keyPress(keyCode);
|
||||
robot.keyRelease(keyCode);
|
||||
}
|
||||
|
||||
private static void clickAt(int x, int y) {
|
||||
robot.delay(1000); // needed for GNOME, to give it some time to update internal state after window showing
|
||||
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