mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8194327: [macos] AWT windows have incorrect main/key window behaviors
Reviewed-by: serb, erikj
This commit is contained in:
parent
f9abf6bee6
commit
d7e59b52c5
@ -64,6 +64,14 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(OPENJDK_TARGET_OS), macosx)
|
||||
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libTestMainKeyWindow := -ObjC
|
||||
BUILD_JDK_JTREG_LIBRARIES_LIBS_libTestMainKeyWindow := -framework JavaVM \
|
||||
-framework Cocoa -framework JavaNativeFoundation
|
||||
else
|
||||
BUILD_JDK_JTREG_EXCLUDE += libTestMainKeyWindow.c
|
||||
endif
|
||||
|
||||
$(eval $(call SetupTestFilesCompilation, BUILD_JDK_JTREG_LIBRARIES, \
|
||||
TYPE := LIBRARY, \
|
||||
SOURCE_DIRS := $(BUILD_JDK_JTREG_NATIVE_SRC), \
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2018, 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
|
||||
@ -262,7 +262,7 @@ AWT_NS_WINDOW_IMPLEMENTATION
|
||||
} else {
|
||||
[self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id) initWithPlatformWindow:(JNFWeakJObjectWrapper *)platformWindow
|
||||
@ -465,7 +465,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
// Tests whether window is blocked by modal dialog/window
|
||||
- (BOOL) isBlocked {
|
||||
BOOL isBlocked = NO;
|
||||
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
|
||||
if (platformWindow != NULL) {
|
||||
@ -473,7 +473,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
isBlocked = JNFCallBooleanMethod(env, platformWindow, jm_isBlocked) == JNI_TRUE ? YES : NO;
|
||||
(*env)->DeleteLocalRef(env, platformWindow);
|
||||
}
|
||||
|
||||
|
||||
return isBlocked;
|
||||
}
|
||||
|
||||
@ -679,7 +679,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
JNFCallVoidMethod(env, platformWindow, jm_windowWillMiniaturize);
|
||||
(*env)->DeleteLocalRef(env, platformWindow);
|
||||
}
|
||||
// Excplicitly make myself a key window to avoid possible
|
||||
// Explicitly make myself a key window to avoid possible
|
||||
// negative visual effects during iconify operation
|
||||
[self.nsWindow makeKeyAndOrderFront:self.nsWindow];
|
||||
[self iconifyChildWindows:YES];
|
||||
@ -713,13 +713,47 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) windowDidBecomeMain: (NSNotification *) notification {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
[AWTToolkit eventCountPlusPlus];
|
||||
#ifdef DEBUG
|
||||
NSLog(@"became main: %d %@ %@", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow]);
|
||||
#endif
|
||||
|
||||
if (![self.nsWindow isKeyWindow]) {
|
||||
[self activateWindowMenuBar];
|
||||
}
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
|
||||
if (platformWindow != NULL) {
|
||||
static JNF_MEMBER_CACHE(jm_windowDidBecomeMain, jc_CPlatformWindow, "windowDidBecomeMain", "()V");
|
||||
JNFCallVoidMethod(env, platformWindow, jm_windowDidBecomeMain);
|
||||
(*env)->DeleteLocalRef(env, platformWindow);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) windowDidBecomeKey: (NSNotification *) notification {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
[AWTToolkit eventCountPlusPlus];
|
||||
#ifdef DEBUG
|
||||
NSLog(@"became key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
|
||||
#endif
|
||||
AWTWindow *opposite = [AWTWindow lastKeyWindow];
|
||||
|
||||
// Finds appropriate menubar in our hierarchy,
|
||||
if (![self.nsWindow isMainWindow]) {
|
||||
[self activateWindowMenuBar];
|
||||
}
|
||||
|
||||
[AWTWindow setLastKeyWindow:nil];
|
||||
|
||||
[self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
|
||||
[self orderChildWindows:YES];
|
||||
}
|
||||
|
||||
- (void) activateWindowMenuBar {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
// Finds appropriate menubar in our hierarchy
|
||||
AWTWindow *awtWindow = self;
|
||||
while (awtWindow.ownerWindow != nil) {
|
||||
awtWindow = awtWindow.ownerWindow;
|
||||
@ -738,26 +772,48 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
|
||||
[CMenuBar activate:menuBar modallyDisabled:isDisabled];
|
||||
|
||||
[AWTWindow setLastKeyWindow:nil];
|
||||
|
||||
[self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
|
||||
[self orderChildWindows:YES];
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
- (CMenuBar *) menuBarForWindow {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
AWTWindow *awtWindow = self;
|
||||
while (awtWindow.ownerWindow != nil) {
|
||||
awtWindow = awtWindow.ownerWindow;
|
||||
}
|
||||
return awtWindow.javaMenuBar;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void) windowDidResignKey: (NSNotification *) notification {
|
||||
// TODO: check why sometimes at start is invoked *not* on AppKit main thread.
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
[AWTToolkit eventCountPlusPlus];
|
||||
[self.javaMenuBar deactivate];
|
||||
|
||||
// In theory, this might cause flickering if the window gaining focus
|
||||
// has its own menu. However, I couldn't reproduce it on practice, so
|
||||
// perhaps this is a non issue.
|
||||
CMenuBar* defaultMenu = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
|
||||
if (defaultMenu != nil) {
|
||||
[CMenuBar activate:defaultMenu modallyDisabled:NO];
|
||||
#ifdef DEBUG
|
||||
NSLog(@"resigned key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
|
||||
#endif
|
||||
if (![self.nsWindow isMainWindow]) {
|
||||
[self deactivateWindow];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) windowDidResignMain: (NSNotification *) notification {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
[AWTToolkit eventCountPlusPlus];
|
||||
#ifdef DEBUG
|
||||
NSLog(@"resigned main: %d %@ %@", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow]);
|
||||
#endif
|
||||
if (![self.nsWindow isKeyWindow]) {
|
||||
[self deactivateWindow];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) deactivateWindow {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
#ifdef DEBUG
|
||||
NSLog(@"deactivating window: %@", [self.nsWindow title]);
|
||||
#endif
|
||||
[self.javaMenuBar deactivate];
|
||||
|
||||
// the new key window
|
||||
NSWindow *keyWindow = [NSApp keyWindow];
|
||||
@ -773,19 +829,6 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
[self orderChildWindows:NO];
|
||||
}
|
||||
|
||||
- (void) windowDidBecomeMain: (NSNotification *) notification {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
[AWTToolkit eventCountPlusPlus];
|
||||
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
|
||||
if (platformWindow != NULL) {
|
||||
static JNF_MEMBER_CACHE(jm_windowDidBecomeMain, jc_CPlatformWindow, "windowDidBecomeMain", "()V");
|
||||
JNFCallVoidMethod(env, platformWindow, jm_windowDidBecomeMain);
|
||||
(*env)->DeleteLocalRef(env, platformWindow);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
[AWTToolkit eventCountPlusPlus];
|
||||
@ -1040,7 +1083,7 @@ JNF_COCOA_ENTER(env);
|
||||
|
||||
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
|
||||
|
||||
if ([nsWindow isKeyWindow]) {
|
||||
if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
|
||||
[window.javaMenuBar deactivate];
|
||||
}
|
||||
|
||||
@ -1051,7 +1094,7 @@ JNF_COCOA_ENTER(env);
|
||||
actualMenuBar = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
|
||||
}
|
||||
|
||||
if ([nsWindow isKeyWindow]) {
|
||||
if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
|
||||
[CMenuBar activate:actualMenuBar modallyDisabled:NO];
|
||||
}
|
||||
}];
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2018, 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
|
||||
@ -31,6 +31,7 @@
|
||||
#import "CMenuBar.h"
|
||||
#import "CMenu.h"
|
||||
#import "ThreadUtilities.h"
|
||||
#import "ApplicationDelegate.h"
|
||||
|
||||
#import "sun_lwawt_macosx_CMenuBar.h"
|
||||
|
||||
@ -101,6 +102,10 @@ static BOOL sSetupHelpMenu = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
NSLog(@"activating menu bar: %@", menubar);
|
||||
#endif
|
||||
|
||||
@synchronized([CMenuBar class]) {
|
||||
sActiveMenuBar = menubar;
|
||||
}
|
||||
@ -184,12 +189,30 @@ static BOOL sSetupHelpMenu = NO;
|
||||
-(void) deactivate {
|
||||
AWT_ASSERT_APPKIT_THREAD;
|
||||
|
||||
BOOL isDeactivated = NO;
|
||||
@synchronized([CMenuBar class]) {
|
||||
sActiveMenuBar = nil;
|
||||
if (sActiveMenuBar == self) {
|
||||
sActiveMenuBar = nil;
|
||||
isDeactivated = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@synchronized(self) {
|
||||
fModallyDisabled = NO;
|
||||
if (isDeactivated) {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"deactivating menu bar: %@", self);
|
||||
#endif
|
||||
|
||||
@synchronized(self) {
|
||||
fModallyDisabled = NO;
|
||||
}
|
||||
|
||||
// In theory, this might cause flickering if the window gaining focus
|
||||
// has its own menu. However, I couldn't reproduce it on practice, so
|
||||
// perhaps this is a non issue.
|
||||
CMenuBar* defaultMenu = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
|
||||
if (defaultMenu != nil) {
|
||||
[CMenuBar activate:defaultMenu modallyDisabled:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2018, 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
|
||||
@ -28,6 +28,7 @@
|
||||
#import "CMenuItem.h"
|
||||
#import "CMenu.h"
|
||||
#import "AWTEvent.h"
|
||||
#import "AWTWindow.h"
|
||||
#import "ThreadUtilities.h"
|
||||
|
||||
#import "java_awt_Event.h"
|
||||
@ -111,12 +112,14 @@
|
||||
}
|
||||
eventKey = [NSString stringWithCharacters: &newChar length: 1];
|
||||
}
|
||||
// The action event can be ignored only if the key window is an AWT window.
|
||||
// Otherwise, the action event is the only notification and must be processed.
|
||||
NSWindow *keyWindow = [NSApp keyWindow];
|
||||
if (keyWindow != nil) {
|
||||
if (keyWindow != nil && [AWTWindow isAWTWindow: keyWindow]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static JNF_CLASS_CACHE(jc_CMenuItem, "sun/lwawt/macosx/CMenuItem");
|
||||
static JNF_MEMBER_CACHE(jm_handleAction, jc_CMenuItem, "handleAction", "(JI)V"); // AWT_THREADING Safe (event)
|
||||
|
||||
@ -126,7 +129,7 @@
|
||||
JNFCallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event)
|
||||
}
|
||||
JNF_COCOA_EXIT(env);
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers {
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define DEBUG 1
|
||||
//#define DEBUG 1
|
||||
|
||||
// number of mouse buttons supported
|
||||
extern int gNumberOfButtons;
|
||||
|
||||
@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 8194327
|
||||
* @summary [macosx] AWT windows have incorrect main/key window behaviors
|
||||
* @author Alan Snyder
|
||||
* @run main/othervm/native TestMainKeyWindow
|
||||
* @requires (os.family == "mac")
|
||||
*/
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Objects;
|
||||
import javax.swing.*;
|
||||
|
||||
public class TestMainKeyWindow
|
||||
{
|
||||
static TestMainKeyWindow theTest;
|
||||
|
||||
KeyStroke commandT = KeyStroke.getKeyStroke(KeyEvent.VK_T, KeyEvent.META_DOWN_MASK);
|
||||
|
||||
int nextX = 130;
|
||||
|
||||
private final MyFrame frame1;
|
||||
private final MyFrame frame2;
|
||||
private final Object COLOR_PANEL = "Color Panel";
|
||||
private final Object NATIVE_WINDOW = "Native Window";
|
||||
|
||||
// these bounds must agree with the native code that creates the windows
|
||||
private Rectangle colorPanelBounds = new Rectangle(130, 300, 225, 400); // approximate is OK
|
||||
private Rectangle nativeWindowBounds = new Rectangle(130, 200, 200, 100);
|
||||
|
||||
private Robot robot;
|
||||
|
||||
private int actionCounter;
|
||||
private Object actionTarget;
|
||||
|
||||
private int failureCount;
|
||||
private boolean isApplicationOpened;
|
||||
|
||||
public TestMainKeyWindow()
|
||||
{
|
||||
System.loadLibrary("testMainKeyWindow");
|
||||
|
||||
JMenuBar defaultMenuBar = createMenuBar("Application", true);
|
||||
Desktop.getDesktop().setDefaultMenuBar(defaultMenuBar);
|
||||
|
||||
setup();
|
||||
|
||||
frame1 = new MyFrame("Frame 1");
|
||||
frame2 = new MyFrame("Frame 2");
|
||||
frame1.setVisible(true);
|
||||
frame2.setVisible(true);
|
||||
|
||||
try {
|
||||
robot = new Robot();
|
||||
robot.setAutoDelay(50);
|
||||
} catch (AWTException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
class MyFrame
|
||||
extends JFrame
|
||||
{
|
||||
public MyFrame(String title)
|
||||
throws HeadlessException
|
||||
{
|
||||
super(title);
|
||||
|
||||
JMenuBar mainMenuBar = createMenuBar(title, true);
|
||||
setJMenuBar(mainMenuBar);
|
||||
setBounds(nextX, 60, 200, 90);
|
||||
nextX += 250;
|
||||
JComponent contentPane = new JPanel();
|
||||
setContentPane(contentPane);
|
||||
contentPane.setLayout(new FlowLayout());
|
||||
contentPane.add(new JCheckBox("foo", true));
|
||||
InputMap inputMap = contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
inputMap.put(commandT, "test");
|
||||
ActionMap actionMap = contentPane.getActionMap();
|
||||
actionMap.put("test", new MyAction(title + " Key"));
|
||||
}
|
||||
}
|
||||
|
||||
private void runTest()
|
||||
{
|
||||
failureCount = 0;
|
||||
robot.waitForIdle();
|
||||
performTest(frame1, false);
|
||||
performTest(frame1, true);
|
||||
performTest(frame2, false);
|
||||
performTest(frame2, true);
|
||||
performTest(NATIVE_WINDOW, false);
|
||||
performTest(NATIVE_WINDOW, true);
|
||||
performTest(COLOR_PANEL, false);
|
||||
if (failureCount > 0) {
|
||||
throw new RuntimeException("Test failed: " + failureCount + " failure(s)");
|
||||
}
|
||||
}
|
||||
|
||||
private void performTest(Object windowIdentification, boolean selectColorPanel)
|
||||
{
|
||||
setupWindows(windowIdentification, selectColorPanel);
|
||||
|
||||
performMenuShortcutTest(windowIdentification, selectColorPanel);
|
||||
performMenuItemTest(windowIdentification, selectColorPanel);
|
||||
|
||||
// test deactivating and reactivating the application
|
||||
// the window state and behavior should be restored
|
||||
|
||||
openOtherApplication();
|
||||
activateApplication();
|
||||
robot.delay(1000);
|
||||
|
||||
performMenuShortcutTest(windowIdentification, selectColorPanel);
|
||||
performMenuItemTest(windowIdentification, selectColorPanel);
|
||||
}
|
||||
|
||||
private void openOtherApplication() {
|
||||
try {
|
||||
String[] cmd = { "/usr/bin/open", "/Applications/System Preferences.app" };
|
||||
Runtime.getRuntime().exec(cmd);
|
||||
if (!isApplicationOpened) {
|
||||
String[] cmd2 = { "/usr/bin/osascript", "-e",
|
||||
"tell application \"System Preferences\" to set bounds of window 1 to {400, 180, 1068, 821}" };
|
||||
Runtime.getRuntime().exec(cmd2);
|
||||
}
|
||||
isApplicationOpened = true;
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Unable to deactivate test application");
|
||||
}
|
||||
robot.delay(1000);
|
||||
}
|
||||
|
||||
private void performMenuShortcutTest(Object windowIdentification, boolean selectColorPanel)
|
||||
{
|
||||
int currentActionCount = actionCounter;
|
||||
|
||||
// Perform the menu shortcut
|
||||
robot.keyPress(KeyEvent.VK_META);
|
||||
robot.keyPress(KeyEvent.VK_T);
|
||||
robot.keyRelease(KeyEvent.VK_T);
|
||||
robot.keyRelease(KeyEvent.VK_META);
|
||||
robot.waitForIdle();
|
||||
|
||||
Object target = waitForAction(currentActionCount + 1);
|
||||
boolean isDirectKey = windowIdentification instanceof Window && !selectColorPanel;
|
||||
Object expectedTarget = getExpectedTarget(windowIdentification, isDirectKey);
|
||||
if (!Objects.equals(target, expectedTarget)) {
|
||||
failureCount++;
|
||||
String configuration = getConfigurationName(windowIdentification, selectColorPanel);
|
||||
System.err.println("***** Menu shortcut test failed for " + configuration + ". Expected: " + expectedTarget + ", Actual: " + target);
|
||||
}
|
||||
}
|
||||
|
||||
private void performMenuItemTest(Object windowIdentification, boolean selectColorPanel)
|
||||
{
|
||||
int currentActionCount = actionCounter;
|
||||
|
||||
// Find the menu on the screen menu bar
|
||||
// The location depends upon the application name which is the name of the first menu.
|
||||
// Unfortunately, the application name can vary based on how the application is run.
|
||||
// The work around is to make the menu and the menu item names very long.
|
||||
|
||||
int menuBarX = 250;
|
||||
int menuBarY = 11;
|
||||
int menuItemX = menuBarX;
|
||||
int menuItemY = 34;
|
||||
|
||||
robot.mouseMove(menuBarX, menuBarY);
|
||||
robot.delay(100);
|
||||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.delay(100);
|
||||
robot.mouseMove(menuItemX, menuItemY);
|
||||
robot.delay(100);
|
||||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.waitForIdle();
|
||||
|
||||
Object target = waitForAction(currentActionCount + 1);
|
||||
Object expectedTarget = getExpectedTarget(windowIdentification, false);
|
||||
if (!Objects.equals(target, expectedTarget)) {
|
||||
failureCount++;
|
||||
String configuration = getConfigurationName(windowIdentification, selectColorPanel);
|
||||
System.err.println("***** Menu item test failed for " + configuration + ". Expected: " + expectedTarget + ", Actual: " + target);
|
||||
}
|
||||
}
|
||||
|
||||
private String getConfigurationName(Object windowIdentification, boolean selectColorPanel)
|
||||
{
|
||||
String name = "Unknown";
|
||||
if (windowIdentification instanceof Window) {
|
||||
Window w = (Window) windowIdentification;
|
||||
name = getWindowTitle(w);
|
||||
} else if (windowIdentification == NATIVE_WINDOW) {
|
||||
name = "Native Window";
|
||||
} else if (windowIdentification == COLOR_PANEL) {
|
||||
name = "Color Panel";
|
||||
}
|
||||
if (selectColorPanel) {
|
||||
return name + " with color panel";
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private Object getExpectedTarget(Object windowIdentification, boolean isDirectKey)
|
||||
{
|
||||
if (windowIdentification instanceof Window) {
|
||||
Window w = (Window) windowIdentification;
|
||||
String title = getWindowTitle(w);
|
||||
if (isDirectKey) {
|
||||
title = title + " Key";
|
||||
}
|
||||
return title;
|
||||
}
|
||||
return "Application";
|
||||
}
|
||||
|
||||
private String getWindowTitle(Window w)
|
||||
{
|
||||
if (w instanceof Frame) {
|
||||
Frame f = (Frame) w;
|
||||
return f.getTitle();
|
||||
}
|
||||
if (w instanceof Dialog) {
|
||||
Dialog d = (Dialog) w;
|
||||
return d.getTitle();
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
private synchronized void registerAction(Object target)
|
||||
{
|
||||
actionCounter++;
|
||||
actionTarget = target;
|
||||
}
|
||||
|
||||
private synchronized Object waitForAction(int count)
|
||||
{
|
||||
try {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (actionCounter == count) {
|
||||
return actionTarget;
|
||||
}
|
||||
if (actionCounter > count) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
wait(100);
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
return "No Action";
|
||||
}
|
||||
|
||||
private void setupWindows(Object windowIdentification, boolean selectColorPanel)
|
||||
{
|
||||
clickOnWindowTitleBar(windowIdentification);
|
||||
if (selectColorPanel) {
|
||||
clickOnWindowTitleBar(COLOR_PANEL);
|
||||
}
|
||||
}
|
||||
|
||||
private void clickOnWindowTitleBar(Object windowIdentification)
|
||||
{
|
||||
Rectangle bounds = getWindowBounds(windowIdentification);
|
||||
int x = bounds.x + 70; // to the right of the stoplight buttons
|
||||
int y = bounds.y + 12; // in the title bar
|
||||
robot.mouseMove(x, y);
|
||||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.waitForIdle();
|
||||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||
robot.waitForIdle();
|
||||
}
|
||||
|
||||
private Rectangle getWindowBounds(Object windowIdentification)
|
||||
{
|
||||
if (windowIdentification instanceof Window) {
|
||||
Window w = (Window) windowIdentification;
|
||||
return w.getBounds();
|
||||
}
|
||||
if (windowIdentification == COLOR_PANEL) {
|
||||
return colorPanelBounds;
|
||||
}
|
||||
if (windowIdentification == NATIVE_WINDOW) {
|
||||
return nativeWindowBounds;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
JMenuBar createMenuBar(String text, boolean isEnabled)
|
||||
{
|
||||
JMenuBar mb = new JMenuBar();
|
||||
// A very long name makes it more likely that the robot will hit the menu
|
||||
JMenu menu = new JMenu("TestTestTestTestTestTestTestTestTestTest");
|
||||
mb.add(menu);
|
||||
JMenuItem item = new JMenuItem("TestTestTestTestTestTestTestTestTestTest");
|
||||
item.setAccelerator(commandT);
|
||||
item.setEnabled(isEnabled);
|
||||
item.addActionListener(ev -> {
|
||||
registerAction(text);
|
||||
});
|
||||
menu.add(item);
|
||||
return mb;
|
||||
}
|
||||
|
||||
void dispose()
|
||||
{
|
||||
frame1.setVisible(false);
|
||||
frame2.setVisible(false);
|
||||
frame1.dispose();
|
||||
frame2.dispose();
|
||||
takedown();
|
||||
Desktop.getDesktop().setDefaultMenuBar(null);
|
||||
if (isApplicationOpened) {
|
||||
try {
|
||||
String[] cmd = { "/usr/bin/osascript", "-e", "tell application \"System Preferences\" to close window 1" };
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
p.waitFor();
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyAction
|
||||
extends AbstractAction
|
||||
{
|
||||
String text;
|
||||
|
||||
public MyAction(String text)
|
||||
{
|
||||
super("Test");
|
||||
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
registerAction(text);
|
||||
}
|
||||
}
|
||||
|
||||
private static native void setup();
|
||||
private static native void takedown();
|
||||
private static native void activateApplication();
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
if (!System.getProperty("os.name").contains("OS X")) {
|
||||
System.out.println("This test is for MacOS only. Automatically passed on other platforms.");
|
||||
return;
|
||||
}
|
||||
|
||||
System.setProperty("apple.laf.useScreenMenuBar", "true");
|
||||
|
||||
try {
|
||||
runSwing(() -> {
|
||||
theTest = new TestMainKeyWindow();
|
||||
});
|
||||
theTest.runTest();
|
||||
} finally {
|
||||
if (theTest != null) {
|
||||
runSwing(() -> {
|
||||
theTest.dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void runSwing(Runnable r)
|
||||
{
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(r);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 <Cocoa/Cocoa.h>
|
||||
#import <JavaNativeFoundation/JavaNativeFoundation.h>
|
||||
|
||||
static NSWindow *testWindow;
|
||||
static NSColorPanel *colorPanel;
|
||||
|
||||
/*
|
||||
* Class: TestMainKeyWindow
|
||||
* Method: setup
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_TestMainKeyWindow_setup(JNIEnv *env, jclass cl)
|
||||
{
|
||||
JNF_COCOA_ENTER(env);
|
||||
|
||||
void (^block)() = ^(){
|
||||
NSScreen *mainScreen = [NSScreen mainScreen];
|
||||
NSRect screenFrame = [mainScreen frame];
|
||||
NSRect frame = NSMakeRect(130, screenFrame.size.height - 280, 200, 100);
|
||||
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
|
||||
NSWindowStyleMask style = NSWindowStyleMaskTitled;
|
||||
#else
|
||||
NSInteger style = NSTitledWindowMask;
|
||||
#endif
|
||||
|
||||
NSRect rect = [NSWindow contentRectForFrameRect:frame styleMask:style];
|
||||
NSBackingStoreType bt = NSBackingStoreBuffered;
|
||||
testWindow = [[[NSWindow alloc] initWithContentRect:rect styleMask:style backing:bt defer:NO] retain];
|
||||
testWindow.title = @"NSWindow";
|
||||
[testWindow setBackgroundColor:[NSColor blueColor]];
|
||||
[testWindow makeKeyAndOrderFront:nil];
|
||||
// Java coordinates are 100, 200
|
||||
|
||||
colorPanel = [[NSColorPanel sharedColorPanel] retain];
|
||||
[colorPanel setReleasedWhenClosed: YES];
|
||||
colorPanel.restorable = NO;
|
||||
[colorPanel setFrameTopLeftPoint: NSMakePoint(130, screenFrame.size.height - 300)];
|
||||
// Java coordinates are 100, 400
|
||||
[colorPanel makeKeyAndOrderFront: nil];
|
||||
};
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
block();
|
||||
} else {
|
||||
[JNFRunLoop performOnMainThreadWaiting:YES withBlock:block];
|
||||
}
|
||||
|
||||
JNF_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: TestMainKeyWindow
|
||||
* Method: takedown
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_TestMainKeyWindow_takedown(JNIEnv *env, jclass cl)
|
||||
{
|
||||
JNF_COCOA_ENTER(env);
|
||||
|
||||
void (^block)() = ^(){
|
||||
if (testWindow != nil) {
|
||||
[testWindow close];
|
||||
testWindow = nil;
|
||||
}
|
||||
if (colorPanel != nil) {
|
||||
[colorPanel orderOut:nil];
|
||||
colorPanel = nil;
|
||||
}
|
||||
};
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
block();
|
||||
} else {
|
||||
[JNFRunLoop performOnMainThreadWaiting:YES withBlock:block];
|
||||
}
|
||||
|
||||
JNF_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: TestMainKeyWindow
|
||||
* Method: activateApplication
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_TestMainKeyWindow_activateApplication
|
||||
(JNIEnv *env, jclass cl)
|
||||
{
|
||||
JNF_COCOA_ENTER(env);
|
||||
|
||||
void (^block)() = ^(){
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
};
|
||||
|
||||
[JNFRunLoop performOnMainThreadWaiting:YES withBlock:block];
|
||||
|
||||
JNF_COCOA_EXIT(env);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user