JBR-2712 Typeahead mechanism doesn't work on Windows

(cherry picked from commits 1a9838082e, f5b6222835, acd7e3b2da, cd6dd5c3cf8556f97f3113cb7d615a92393b57bf(partially), e8bbd8ffdd90f57cd12d7d7e89188be97ee4be0b(partially))
This commit is contained in:
Dmitry Batrak
2020-10-02 12:09:26 +03:00
committed by alexey.ushakov@jetbrains.com
parent 7a91294571
commit 3c6aaba7af
23 changed files with 727 additions and 200 deletions

View File

@@ -928,7 +928,7 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight(
getTarget(), lightweightChild, temporary,
focusedWindowChangeAllowed, time, cause);
focusedWindowChangeAllowed, time, cause, false);
switch (result) {
case LWKeyboardFocusManagerPeer.SNFH_FAILURE:
return false;
@@ -982,9 +982,7 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
Component focusOwner = kfmPeer.getCurrentFocusOwner();
return LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
getTarget(), temporary,
focusedWindowChangeAllowed,
time, cause, focusOwner);
getTarget(), false, cause, focusOwner);
case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED:
return true;

View File

@@ -121,10 +121,11 @@ public abstract class KeyboardFocusManager
boolean temporary,
boolean focusedWindowChangeAllowed,
long time,
FocusEvent.Cause cause)
FocusEvent.Cause cause,
boolean highPriorityEvents)
{
return KeyboardFocusManager.shouldNativelyFocusHeavyweight(
heavyweight, descendant, temporary, focusedWindowChangeAllowed, time, cause);
heavyweight, descendant, temporary, focusedWindowChangeAllowed, time, cause, highPriorityEvents);
}
public boolean processSynchronousLightweightTransfer(Component heavyweight,
Component descendant,
@@ -2381,7 +2382,8 @@ public abstract class KeyboardFocusManager
*/
static int shouldNativelyFocusHeavyweight
(Component heavyweight, Component descendant, boolean temporary,
boolean focusedWindowChangeAllowed, long time, FocusEvent.Cause cause)
boolean focusedWindowChangeAllowed, long time, FocusEvent.Cause cause,
boolean highPriorityEvents)
{
if (log.isLoggable(PlatformLogger.Level.FINE)) {
if (heavyweight == null) {
@@ -2450,17 +2452,22 @@ public abstract class KeyboardFocusManager
new FocusEvent(currentFocusOwner,
FocusEvent.FOCUS_LOST,
temporary, descendant, cause);
// Fix 5028014. Rolled out.
// SunToolkit.postPriorityEvent(currentFocusOwnerEvent);
SunToolkit.postEvent(currentFocusOwner.appContext,
currentFocusOwnerEvent);
if (highPriorityEvents) {
SunToolkit.postPriorityEvent(currentFocusOwnerEvent);
} else {
SunToolkit.postEvent(currentFocusOwner.appContext,
currentFocusOwnerEvent);
}
}
FocusEvent newFocusOwnerEvent =
new FocusEvent(descendant, FocusEvent.FOCUS_GAINED,
temporary, currentFocusOwner, cause);
// Fix 5028014. Rolled out.
// SunToolkit.postPriorityEvent(newFocusOwnerEvent);
SunToolkit.postEvent(descendant.appContext, newFocusOwnerEvent);
if (highPriorityEvents) {
SunToolkit.postPriorityEvent(newFocusOwnerEvent);
} else {
SunToolkit.postEvent(descendant.appContext,
newFocusOwnerEvent);
}
if (focusLog.isLoggable(PlatformLogger.Level.FINEST))
focusLog.finest("2. SNFH_HANDLED for {0}", String.valueOf(descendant));

View File

@@ -426,7 +426,8 @@ public final class AWTAccessor {
boolean temporary,
boolean focusedWindowChangeAllowed,
long time,
Cause cause);
Cause cause,
boolean highPriorityEvents);
/**
* Delivers focus for the lightweight descendant of the heavyweight
* synchronously.

View File

@@ -107,9 +107,7 @@ public abstract class KeyboardFocusManagerPeerImpl implements KeyboardFocusManag
@SuppressWarnings("deprecation")
public static boolean deliverFocus(Component lightweightChild,
Component target,
boolean temporary,
boolean focusedWindowChangeAllowed,
long time,
boolean highPriority,
FocusEvent.Cause cause,
Component currentFocusOwner) // provided by the descendant peers
{
@@ -128,7 +126,11 @@ public abstract class KeyboardFocusManagerPeerImpl implements KeyboardFocusManag
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("Posting focus event: " + fl);
}
SunToolkit.postEvent(SunToolkit.targetToAppContext(currentOwner), fl);
if (highPriority) {
SunToolkit.postPriorityEvent(fl);
} else {
SunToolkit.postEvent(SunToolkit.targetToAppContext(currentOwner), fl);
}
}
FocusEvent fg = new FocusEvent(lightweightChild, FocusEvent.FOCUS_GAINED,
@@ -137,7 +139,11 @@ public abstract class KeyboardFocusManagerPeerImpl implements KeyboardFocusManag
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("Posting focus event: " + fg);
}
SunToolkit.postEvent(SunToolkit.targetToAppContext(lightweightChild), fg);
if (highPriority) {
SunToolkit.postPriorityEvent(fg);
} else {
SunToolkit.postEvent(SunToolkit.targetToAppContext(lightweightChild), fg);
}
return true;
}
@@ -152,11 +158,12 @@ public abstract class KeyboardFocusManagerPeerImpl implements KeyboardFocusManag
boolean temporary,
boolean focusedWindowChangeAllowed,
long time,
FocusEvent.Cause cause)
FocusEvent.Cause cause,
boolean highPriorityEvents)
{
return KfmAccessor.instance.shouldNativelyFocusHeavyweight(
heavyweight, descendant, temporary, focusedWindowChangeAllowed,
time, cause);
time, cause, highPriorityEvents);
}
public static void removeLastFocusRequest(Component heavyweight) {

View File

@@ -473,6 +473,14 @@ public abstract class SunToolkit extends Toolkit
* Post AWTEvent of high priority.
*/
public static void postPriorityEvent(final AWTEvent e) {
if (e.getID() == WindowEvent.WINDOW_LOST_FOCUS &&
e instanceof TimedWindowEvent)
{
TimedWindowEvent twe = (TimedWindowEvent)e;
((SunToolkit)Toolkit.getDefaultToolkit()).
setWindowDeactivationTime((Window)twe.getSource(), twe.getWhen());
}
PeerEvent pe = new PeerEvent(Toolkit.getDefaultToolkit(), new Runnable() {
@Override
public void run() {

View File

@@ -259,7 +259,7 @@ public class XComponentPeer extends XWindow implements ComponentPeer, DropTarget
int result = XKeyboardFocusManagerPeer.
shouldNativelyFocusHeavyweight(target, lightweightChild,
temporary, focusedWindowChangeAllowed,
time, cause);
time, cause, false);
switch (result) {
case XKeyboardFocusManagerPeer.SNFH_FAILURE:

View File

@@ -217,7 +217,7 @@ public class XEmbedChildProxyPeer implements ComponentPeer, XEventDispatcher{
{
int result = XKeyboardFocusManagerPeer
.shouldNativelyFocusHeavyweight(proxy, lightweightChild,
temporary, false, time, cause);
temporary, false, time, cause, false);
switch (result) {
case XKeyboardFocusManagerPeer.SNFH_FAILURE:

View File

@@ -105,9 +105,7 @@ public class XKeyboardFocusManagerPeer extends KeyboardFocusManagerPeerImpl {
{
return KeyboardFocusManagerPeerImpl.deliverFocus(lightweightChild,
target,
temporary,
focusedWindowChangeAllowed,
time,
false,
cause,
getInstance().getCurrentFocusOwner());
}

View File

@@ -726,7 +726,7 @@ public abstract class WComponentPeer extends WObjectPeer
int result = WKeyboardFocusManagerPeer
.shouldNativelyFocusHeavyweight((Component)target, lightweightChild,
temporary, focusedWindowChangeAllowed,
time, cause);
time, cause, true);
switch (result) {
case WKeyboardFocusManagerPeer.SNFH_FAILURE:

View File

@@ -80,9 +80,7 @@ final class WKeyboardFocusManagerPeer extends KeyboardFocusManagerPeerImpl {
// TODO: do something to eliminate this forwarding
return KeyboardFocusManagerPeerImpl.deliverFocus(lightweightChild,
target,
temporary,
focusedWindowChangeAllowed,
time,
true,
cause,
getNativeFocusOwner());
}

View File

@@ -101,6 +101,7 @@ HWND AwtComponent::sm_focusOwner = NULL;
HWND AwtComponent::sm_focusedWindow = NULL;
BOOL AwtComponent::sm_bMenuLoop = FALSE;
BOOL AwtComponent::sm_inSynthesizeFocus = FALSE;
BOOL AwtComponent::sm_priorityFocusEvents = FALSE;
/************************************************************************/
// Struct for _Reshape() and ReshapeNoCheck() methods
@@ -1290,6 +1291,7 @@ void SpyWinMessage(HWND hwnd, UINT message, LPCTSTR szComment) {
WIN_MSG(WM_AWT_COMPONENT_HIDE)
WIN_MSG(WM_AWT_COMPONENT_SETFOCUS)
WIN_MSG(WM_AWT_WINDOW_SETACTIVE)
WIN_MSG(WM_AWT_WINDOW_TOFRONT)
WIN_MSG(WM_AWT_LIST_SETMULTISELECT)
WIN_MSG(WM_AWT_HANDLE_EVENT)
WIN_MSG(WM_AWT_PRINT_COMPONENT)
@@ -1955,24 +1957,38 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
* there.
*/
case WM_AWT_COMPONENT_SHOW:
sm_priorityFocusEvents = TRUE;
Show();
sm_priorityFocusEvents = FALSE;
mr = mrConsume;
break;
case WM_AWT_COMPONENT_HIDE:
sm_priorityFocusEvents = TRUE;
Hide();
sm_priorityFocusEvents = FALSE;
mr = mrConsume;
break;
case WM_AWT_COMPONENT_SETFOCUS:
sm_priorityFocusEvents = TRUE;
if ((BOOL)wParam) {
retValue = SynthesizeWmSetFocus(GetHWnd(), NULL);
} else {
retValue = SynthesizeWmKillFocus(GetHWnd(), NULL);
}
sm_priorityFocusEvents = FALSE;
mr = mrConsume;
break;
case WM_AWT_WINDOW_SETACTIVE:
sm_priorityFocusEvents = TRUE;
retValue = (LRESULT)((AwtWindow*)this)->AwtSetActiveWindow((BOOL)wParam);
sm_priorityFocusEvents = FALSE;
mr = mrConsume;
break;
case WM_AWT_WINDOW_TOFRONT:
sm_priorityFocusEvents = TRUE;
((AwtWindow*)this)->ToFront();
sm_priorityFocusEvents = FALSE;
mr = mrConsume;
break;

View File

@@ -693,6 +693,7 @@ public:
static void *GetNativeFocusOwner();
static BOOL sm_inSynthesizeFocus;
static BOOL sm_priorityFocusEvents;
// Execute on Toolkit only.
INLINE static LRESULT SynthesizeWmSetFocus(HWND targetHWnd, HWND oppositeHWnd) {

View File

@@ -519,7 +519,9 @@ MsgRouting AwtDialog::WmEndModal()
jobject peer = GetPeer(env);
jobject target = GetTarget(env);
if (::GetForegroundWindow() == GetHWnd()) {
sm_priorityFocusEvents = TRUE;
ModalActivateNextWindow(GetHWnd(), target, peer);
sm_priorityFocusEvents = FALSE;
}
// hide the dialog
SendMessage(WM_AWT_COMPONENT_HIDE);

View File

@@ -286,7 +286,7 @@ AwtWindow::Grab() {
// we shouldn't perform grab in this case (see 4841881 & 6539458)
Ungrab();
} else if (GetHWnd() != AwtComponent::GetFocusedWindow()) {
_ToFront(env->NewGlobalRef(GetPeer(env)));
_ToFront(env->NewGlobalRef(GetPeer(env)), FALSE);
// Global ref was deleted in _ToFront
}
}
@@ -1599,6 +1599,34 @@ void AwtWindow::SendComponentEvent(jint eventId)
env->DeleteLocalRef(event);
}
static void SendPriorityEvent(jobject event) {
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
static jclass toolkitClass;
if (toolkitClass == NULL) {
toolkitClass = env->FindClass("sun/awt/SunToolkit");
if (toolkitClass != NULL) {
toolkitClass = (jclass)env->NewGlobalRef(toolkitClass);
}
if (toolkitClass == NULL) {
return;
}
}
static jmethodID postPriorityEventMID;
if (postPriorityEventMID == NULL) {
postPriorityEventMID =
env->GetStaticMethodID(toolkitClass, "postPriorityEvent",
"(Ljava/awt/AWTEvent;)V");
DASSERT(postPriorityEventMID);
if (postPriorityEventMID == NULL) {
return;
}
}
env->CallStaticVoidMethod(toolkitClass, postPriorityEventMID, event);
}
void AwtWindow::SendWindowEvent(jint id, HWND opposite,
jint oldState, jint newState)
{
@@ -1629,25 +1657,6 @@ void AwtWindow::SendWindowEvent(jint id, HWND opposite,
}
}
static jclass sequencedEventCls;
if (sequencedEventCls == NULL) {
jclass sequencedEventClsLocal
= env->FindClass("java/awt/SequencedEvent");
DASSERT(sequencedEventClsLocal);
CHECK_NULL(sequencedEventClsLocal);
sequencedEventCls =
(jclass)env->NewGlobalRef(sequencedEventClsLocal);
env->DeleteLocalRef(sequencedEventClsLocal);
}
static jmethodID sequencedEventConst;
if (sequencedEventConst == NULL) {
sequencedEventConst =
env->GetMethodID(sequencedEventCls, "<init>",
"(Ljava/awt/AWTEvent;)V");
CHECK_NULL(sequencedEventConst);
}
static jclass windowCls = NULL;
if (windowCls == NULL) {
jclass windowClsLocal = env->FindClass("java/awt/Window");
@@ -1701,20 +1710,15 @@ void AwtWindow::SendWindowEvent(jint id, HWND opposite,
env->DeleteLocalRef(target); target = NULL;
CHECK_NULL(event);
if (id == java_awt_event_WindowEvent_WINDOW_GAINED_FOCUS ||
id == java_awt_event_WindowEvent_WINDOW_LOST_FOCUS)
if (AwtComponent::sm_priorityFocusEvents &&
(id == java_awt_event_WindowEvent_WINDOW_GAINED_FOCUS ||
id == java_awt_event_WindowEvent_WINDOW_LOST_FOCUS))
{
jobject sequencedEvent = env->NewObject(sequencedEventCls,
sequencedEventConst,
event);
DASSERT(!safe_ExceptionOccurred(env));
DASSERT(sequencedEvent != NULL);
env->DeleteLocalRef(event);
event = sequencedEvent;
SendPriorityEvent(event);
} else {
SendEvent(event);
}
SendEvent(event);
env->DeleteLocalRef(event);
}
@@ -2441,7 +2445,25 @@ ret:
return result;
}
void AwtWindow::_ToFront(void *param)
void AwtWindow::ToFront() {
if (::IsWindow(GetHWnd())) {
UINT flags = SWP_NOMOVE|SWP_NOSIZE;
BOOL focusable = IsFocusableWindow();
BOOL autoRequestFocus = IsAutoRequestFocus();
if (!focusable || !autoRequestFocus)
{
flags = flags|SWP_NOACTIVATE;
}
::SetWindowPos(GetHWnd(), HWND_TOP, 0, 0, 0, 0, flags);
if (focusable && autoRequestFocus)
{
::SetForegroundWindow(GetHWnd());
}
}
}
void AwtWindow::_ToFront(void *param, BOOL wait)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
@@ -2454,24 +2476,20 @@ void AwtWindow::_ToFront(void *param)
w = (AwtWindow *)pData;
if (::IsWindow(w->GetHWnd()))
{
UINT flags = SWP_NOMOVE|SWP_NOSIZE;
BOOL focusable = w->IsFocusableWindow();
BOOL autoRequestFocus = w->IsAutoRequestFocus();
if (!focusable || !autoRequestFocus)
{
flags = flags|SWP_NOACTIVATE;
}
::SetWindowPos(w->GetHWnd(), HWND_TOP, 0, 0, 0, 0, flags);
if (focusable && autoRequestFocus)
{
::SetForegroundWindow(w->GetHWnd());
if (wait) {
w->SendMessage(WM_AWT_WINDOW_TOFRONT, 0, 0);
} else {
w->ToFront();
}
}
ret:
env->DeleteGlobalRef(self);
}
static void _ToFrontWait(void *param) {
AwtWindow::_ToFront(param, TRUE);
}
void AwtWindow::_ToBack(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
@@ -3458,7 +3476,7 @@ Java_sun_awt_windows_WWindowPeer__1toFront(JNIEnv *env, jobject self)
{
TRY;
AwtToolkit::GetInstance().SyncCall(AwtWindow::_ToFront,
AwtToolkit::GetInstance().SyncCall(_ToFrontWait,
env->NewGlobalRef(self));
// global ref is deleted in _ToFront()

View File

@@ -218,13 +218,15 @@ public:
void UpdateWindow(JNIEnv* env, jintArray data, int width, int height,
HBITMAP hNewBitmap = NULL);
void ToFront();
INLINE virtual BOOL IsTopLevel() { return TRUE; }
static AwtWindow * GetGrabbedWindow() { return m_grabbedWindow; }
static void FlashWindowEx(HWND hWnd, UINT count, DWORD timeout, DWORD flags);
// some methods invoked on Toolkit thread
static void _ToFront(void *param);
static void _ToFront(void *param, BOOL wait);
static void _ToBack(void *param);
static void _Grab(void *param);
static void _Ungrab(void *param);

View File

@@ -207,6 +207,7 @@ enum {
WM_AWT_COMPONENT_HIDE,
WM_AWT_COMPONENT_SETFOCUS,
WM_AWT_WINDOW_SETACTIVE,
WM_AWT_WINDOW_TOFRONT,
WM_AWT_LIST_SETMULTISELECT,
WM_AWT_HANDLE_EVENT,
WM_AWT_PRINT_COMPONENT,

View File

@@ -293,15 +293,16 @@ public class EventQueueMonitor
case MouseEvent.MOUSE_MOVED:
case MouseEvent.MOUSE_DRAGGED:
case FocusEvent.FOCUS_GAINED:
case WindowEvent.WINDOW_ACTIVATED:
case WindowEvent.WINDOW_DEACTIVATED:
queueComponentEvent((ComponentEvent) theEvent);
break;
case WindowEvent.WINDOW_ACTIVATED:
// Dialogs fire WINDOW_ACTIVATED and FOCUS_GAINED events
// before WINDOW_OPENED so we need to add topLevelListeners
// for the dialog when it is first activated to get a
// focus gained event for the focus component in the dialog.
case WindowEvent.WINDOW_GAINED_FOCUS:
// WINDOW_GAINED_FOCUS, WINDOW_ACTIVATED and FOCUS_GAINED events
// are fired before WINDOW_OPENED so we need to add topLevelListeners
// for the window when it is first focused to get a
// focus gained event for the focus component in it.
if (theEvent instanceof ComponentEvent) {
ComponentEvent ce = (ComponentEvent)theEvent;
if (ce.getComponent() instanceof Window) {
@@ -312,7 +313,6 @@ public class EventQueueMonitor
EventQueueMonitor.addTopLevelWindow(ce.getComponent());
}
}
queueComponentEvent((ComponentEvent) theEvent);
break;
// handle WINDOW_OPENED and WINDOW_CLOSED events synchronously

View File

@@ -1,117 +0,0 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* 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.
*/
/*
* @test
* @key headful
* @bug 6981400
* @summary Tabbing between textfiled do not work properly when ALT+TAB
* @author anton.tarasov
* @library ../../regtesthelpers
* @build Util
* @run main Test2
*/
// A focus request made after a char is typed ahead shouldn't affect the char's target component.
import java.awt.*;
import java.awt.event.*;
import test.java.awt.regtesthelpers.Util;
public class Test2 {
static Frame f = new Frame("frame");
static TextArea t0 = new TextArea(1, 10) { public String toString() { return "[TA-0]";} };
static TextArea t1 = new TextArea(1, 10) { public String toString() { return "[TA-1]";} };
static TextArea t2 = new TextArea(1, 10) { public String toString() { return "[TA-2]";} };
static volatile boolean passed = true;
static Robot robot;
public static void main(String[] args) {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent e) {
System.out.println(e);
if (e.getID() == KeyEvent.KEY_TYPED) {
if (e.getSource() != t1) {
passed = false;
throw new RuntimeException("Test failed: the key event has wrong source: " + e);
}
}
}
}, FocusEvent.FOCUS_EVENT_MASK | KeyEvent.KEY_EVENT_MASK);
try {
robot = new Robot();
} catch (AWTException ex) {
throw new RuntimeException("Error: can't create Robot");
}
f.add(t0);
f.add(t1);
f.add(t2);
f.setLayout(new FlowLayout());
f.pack();
t0.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
try {
Thread.sleep(3000);
} catch (Exception ex) {}
}
});
// The request shouldn't affect the key event delivery.
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (Exception ex) {}
System.out.println("requesting focus to " + t2);
t2.requestFocus();
}
}).start();
f.setVisible(true);
Util.waitForIdle(robot);
test();
if (passed) System.out.println("\nTest passed.");
}
static void test() {
Util.clickOnComp(t1, robot);
// The key event should be eventually delivered to t1.
robot.delay(50);
robot.keyPress(KeyEvent.VK_A);
robot.delay(50);
robot.keyRelease(KeyEvent.VK_A);
Util.waitForIdle(robot);
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2000-2020 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
import java.util.concurrent.locks.LockSupport;
/**
* @test
* @summary Test for the case which was broken during development for JBR-2712 (Typeahead mechanism doesn't work on Windows)
* @key headful
*/
public class ModalDialogFromMenuTest {
private static final CompletableFuture<Boolean> initFinished = new CompletableFuture<>();
private static final CompletableFuture<Boolean> menuItemShown = new CompletableFuture<>();
private static final CompletableFuture<Boolean> typedInDialog = new CompletableFuture<>();
private static Robot robot;
private static JFrame frame;
private static JTextField frameField;
private static JMenuItem menuItem;
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(ModalDialogFromMenuTest::initUI);
initFinished.get(10, TimeUnit.SECONDS);
rightClickOn(frameField);
menuItemShown.get(10, TimeUnit.SECONDS);
clickOn(menuItem);
pressAndRelease(KeyEvent.VK_A);
typedInDialog.get(10, TimeUnit.SECONDS);
} finally {
SwingUtilities.invokeAndWait(ModalDialogFromMenuTest::disposeUI);
}
}
private static void initUI() {
frame = new JFrame("ModalDialogFromMenuTest");
frame.addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(WindowEvent e) {
initFinished.complete(true);
}
});
frameField = new JTextField(20);
JPopupMenu menu = new JPopupMenu();
menuItem = new JMenuItem(new AbstractAction("open dialog") {
@Override
public void actionPerformed(ActionEvent e) {
JDialog d = new JDialog(frame, true);
JTextField dialogField = new JTextField(10);
dialogField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
typedInDialog.complete(true);
}
@Override
public void removeUpdate(DocumentEvent e) {}
@Override
public void changedUpdate(DocumentEvent e) {}
});
d.add(dialogField);
d.pack();
d.setVisible(true);
}
});
menuItem.addHierarchyListener(e -> {
if (menuItem.isShowing()) menuItemShown.complete(true);
});
menu.add(menuItem);
frameField.setComponentPopupMenu(menu);
frame.add(frameField);
frame.pack();
frame.setVisible(true);
}
private static void disposeUI() {
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, int buttons) {
robot.delay(1000); // needed for GNOME, to give it some time to update internal state after window showing
robot.mouseMove(x, y);
robot.mousePress(buttons);
robot.mouseRelease(buttons);
}
private static void clickOn(Component component, int buttons) {
Point location = component.getLocationOnScreen();
clickAt(location.x + component.getWidth() / 2, location.y + component.getHeight() / 2, buttons);
}
private static void clickOn(Component component) {
clickOn(component, InputEvent.BUTTON1_DOWN_MASK);
}
private static void rightClickOn(Component component) {
clickOn(component, InputEvent.BUTTON3_DOWN_MASK);
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2000-2020 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* @test
* @summary Regression test for JBR-2843 No caret in merge window
* @key headful
*/
public class SequentialModalDialogsTest {
private static final CompletableFuture<Boolean> initFinished = new CompletableFuture<>();
private static final CompletableFuture<Boolean> secondDialogShown = new CompletableFuture<>();
private static final CompletableFuture<Boolean> typedInDialog = new CompletableFuture<>();
private static Robot robot;
private static JFrame frame;
private static JButton frameButton;
private static JTextField windowField;
public static void main(String[] args) throws Exception {
robot = new Robot();
try {
SwingUtilities.invokeAndWait(SequentialModalDialogsTest::initUI);
initFinished.get(10, TimeUnit.SECONDS);
clickOn(frameButton);
secondDialogShown.get(10, TimeUnit.SECONDS);
pressAndRelease(KeyEvent.VK_ENTER);
typedInDialog.get(10, TimeUnit.SECONDS);
} finally {
SwingUtilities.invokeAndWait(SequentialModalDialogsTest::disposeUI);
}
}
private static void initUI() {
frame = new JFrame("SequentialModalDialogsTest");
frameButton = new JButton("Open dialogs");
frameButton.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
initFinished.complete(true);
}
});
frameButton.addActionListener(e -> {
showDialogs();
});
frame.add(frameButton);
frame.pack();
frame.setVisible(true);
}
private static void showDialogs() {
JDialog d1 = new JDialog(frame, true);
JTextField t1 = new JTextField("dialog 1");
t1.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
// using invokeLater to emulate some moment after dialog showing
SwingUtilities.invokeLater(() -> d1.setVisible(false));
}
});
d1.add(t1);
d1.pack();
d1.setVisible(true);
JDialog d2 = new JDialog(frame, true);
JTextField t2 = new JTextField("dialog 2");
t2.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
// using invokeLater to emulate some moment after dialog showing
SwingUtilities.invokeLater(() -> secondDialogShown.complete(true));
}
});
t2.addActionListener(e -> typedInDialog.complete(true));
d2.add(t2);
d2.pack();
d2.setVisible(true);
}
private static void disposeUI() {
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);
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2000-2020 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
import java.util.concurrent.locks.LockSupport;
/**
* @test
* @summary Regression test for JBR-2712 Typeahead mechanism doesn't work on Windows
* @key headful
*/
public class TypeaheadRequestFocusTest {
private static final CompletableFuture<Boolean> initFinished = new CompletableFuture<>();
private static final CompletableFuture<Boolean> typedInPopup = new CompletableFuture<>();
private static Robot robot;
private static JFrame frame;
private static JTextField frameField;
private static JTextField windowField;
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(TypeaheadRequestFocusTest::initUI);
initFinished.get(10, TimeUnit.SECONDS);
clickOn(frameField);
SwingUtilities.invokeAndWait(TypeaheadRequestFocusTest::showPopup);
pressAndRelease(KeyEvent.VK_ENTER);
pressAndRelease(KeyEvent.VK_A);
typedInPopup.get(10, TimeUnit.SECONDS);
} finally {
SwingUtilities.invokeAndWait(TypeaheadRequestFocusTest::disposeUI);
}
}
private static void initUI() {
frame = new JFrame("TypeaheadRequestFocusTest");
frame.addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(WindowEvent e) {
initFinished.complete(true);
}
});
frameField = new JTextField(20);
frameField.addActionListener(e -> {
LockSupport.parkNanos(1_000_000_000L);
windowField.requestFocus();
});
frame.add(frameField);
frame.pack();
frame.setVisible(true);
}
private static void showPopup() {
JWindow window = new JWindow(frame);
windowField = new JTextField(20);
windowField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
typedInPopup.complete(true);
}
@Override
public void removeUpdate(DocumentEvent e) {}
@Override
public void changedUpdate(DocumentEvent e) {}
});
window.add(windowField);
window.pack();
window.setAutoRequestFocus(false);
window.setVisible(true);
window.setAutoRequestFocus(true);
}
private static void disposeUI() {
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);
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2000-2020 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
import java.util.concurrent.locks.LockSupport;
/**
* @test
* @summary Regression test for JBR-2712 Typeahead mechanism doesn't work on Windows
* @key headful
*/
public class TypeaheadSetVisibleTest {
private static final CompletableFuture<Boolean> initFinished = new CompletableFuture<>();
private static final CompletableFuture<Boolean> typedInPopup = new CompletableFuture<>();
private static Robot robot;
private static JFrame frame;
private static JTextField frameField;
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(TypeaheadSetVisibleTest::initUI);
initFinished.get(10, TimeUnit.SECONDS);
clickOn(frameField);
pressAndRelease(KeyEvent.VK_ENTER);
pressAndRelease(KeyEvent.VK_A);
typedInPopup.get(10, TimeUnit.SECONDS);
} finally {
SwingUtilities.invokeAndWait(TypeaheadSetVisibleTest::disposeUI);
}
}
private static void initUI() {
frame = new JFrame("TypeaheadSetVisibleTest");
frame.addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(WindowEvent e) {
initFinished.complete(true);
}
});
frameField = new JTextField(20);
frameField.addActionListener(e -> {
LockSupport.parkNanos(1_000_000_000L);
JWindow window = new JWindow(frame);
JTextField windowField = new JTextField(20);
windowField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
typedInPopup.complete(true);
}
@Override
public void removeUpdate(DocumentEvent e) {}
@Override
public void changedUpdate(DocumentEvent e) {}
});
window.add(windowField);
window.pack();
window.setVisible(true);
});
frame.add(frameField);
frame.pack();
frame.setVisible(true);
}
private static void disposeUI() {
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);
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2000-2020 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
import java.util.concurrent.locks.LockSupport;
/**
* @test
* @summary Regression test for JBR-2712 Typeahead mechanism doesn't work on Windows
* @key headful
*/
public class TypeaheadToFrontTest {
private static final CompletableFuture<Boolean> initFinished = new CompletableFuture<>();
private static final CompletableFuture<Boolean> typedInPopup = new CompletableFuture<>();
private static Robot robot;
private static JFrame frame;
private static JTextField frameField;
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(TypeaheadToFrontTest::initUI);
initFinished.get(10, TimeUnit.SECONDS);
clickOn(frameField);
SwingUtilities.invokeAndWait(TypeaheadToFrontTest::showPopup);
pressAndRelease(KeyEvent.VK_ENTER);
pressAndRelease(KeyEvent.VK_A);
typedInPopup.get(10, TimeUnit.SECONDS);
} finally {
SwingUtilities.invokeAndWait(TypeaheadToFrontTest::disposeUI);
}
}
private static void initUI() {
frame = new JFrame("TypeaheadToFrontTest");
frame.addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(WindowEvent e) {
initFinished.complete(true);
}
});
frameField = new JTextField(20);
frameField.addActionListener(e -> {
LockSupport.parkNanos(1_000_000_000L);
window.toFront();
});
frame.add(frameField);
frame.pack();
frame.setVisible(true);
}
private static void showPopup() {
window = new JWindow(frame);
JTextField windowField = new JTextField(20);
windowField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
typedInPopup.complete(true);
}
@Override
public void removeUpdate(DocumentEvent e) {}
@Override
public void changedUpdate(DocumentEvent e) {}
});
window.add(windowField);
window.pack();
window.setAutoRequestFocus(false);
window.setVisible(true);
window.setAutoRequestFocus(true);
}
private static void disposeUI() {
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);
}
}