Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,9 @@ public List<Menu> getMenus() {
Application.checkEventThread();
return Collections.unmodifiableList(menus);
}

public boolean handleKeyEvent(int code, int modifiers) {
Application.checkEventThread();
return delegate.handleKeyEvent(code, modifiers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ public interface MenuBarDelegate {
// removes a submenu at {@code pos} which delegate is {@code menu} parameter
public boolean remove(MenuDelegate menu, int pos);
public long getNativeMenu();
// Returns true if the key event was processed
public default boolean handleKeyEvent(int code, int modifiers) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ class MacMenuBarDelegate implements MenuBarDelegate {
return true;
}

private native boolean _handleKeyEvent(long menubarPtr, int code, int modifiers);
@Override
public boolean handleKeyEvent(int code, int modifiers) {
return _handleKeyEvent(ptr, code, modifiers);
}

@Override public long getNativeMenu() {
return ptr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@

package com.sun.javafx.stage;

import com.sun.javafx.tk.Toolkit;

import com.sun.javafx.event.BasicEventDispatcher;
import com.sun.javafx.event.CompositeEventDispatcher;
import com.sun.javafx.event.EventHandlerManager;
import com.sun.javafx.event.EventRedirector;

import javafx.event.Event;
import javafx.scene.input.KeyEvent;
import javafx.stage.Window;

/**
Expand All @@ -38,29 +42,73 @@
* and then through {@code EventHandlerManager}.
*/
public class WindowEventDispatcher extends CompositeEventDispatcher {

static class SystemMenuHandler extends BasicEventDispatcher {
private enum SupportedState {
TRUE,
FALSE,
UNKNOWN
};

private SupportedState supported = SupportedState.UNKNOWN;

@Override
public Event dispatchBubblingEvent(Event event) {
if (supported == SupportedState.UNKNOWN) {
var systemMenu = Toolkit.getToolkit().getSystemMenu();
if (systemMenu != null && systemMenu.isSupported()) {
supported = SupportedState.TRUE;
} else {
supported = SupportedState.FALSE;
}
}
if (supported == SupportedState.TRUE && event.getEventType() == KeyEvent.KEY_PRESSED && event instanceof KeyEvent ke) {
Toolkit.getToolkit().getSystemMenu().handleKeyEvent(ke);
}
return event;
}
}

private final EventRedirector eventRedirector;

private final WindowCloseRequestHandler windowCloseRequestHandler;

private final EventHandlerManager eventHandlerManager;

private final SystemMenuHandler systemMenuHandler;

public WindowEventDispatcher(final Window window) {
this(new EventRedirector(window),
new WindowCloseRequestHandler(window),
new EventHandlerManager(window));

new EventHandlerManager(window),
new SystemMenuHandler());
}

public WindowEventDispatcher(
final EventRedirector eventRedirector,
final WindowCloseRequestHandler windowCloseRequestHandler,
final EventHandlerManager eventHandlerManager) {
this(eventRedirector,
windowCloseRequestHandler,
eventHandlerManager,
null);
}

private WindowEventDispatcher(
final EventRedirector eventRedirector,
final WindowCloseRequestHandler windowCloseRequestHandler,
final EventHandlerManager eventHandlerManager,
final SystemMenuHandler systemMenuHandler) {
this.eventRedirector = eventRedirector;
this.windowCloseRequestHandler = windowCloseRequestHandler;
this.eventHandlerManager = eventHandlerManager;
this.systemMenuHandler = systemMenuHandler;

eventRedirector.insertNextDispatcher(windowCloseRequestHandler);
windowCloseRequestHandler.insertNextDispatcher(eventHandlerManager);
if (systemMenuHandler != null) {
eventHandlerManager.insertNextDispatcher(systemMenuHandler);
}
}

public final EventRedirector getEventRedirector() {
Expand All @@ -82,6 +130,9 @@ public BasicEventDispatcher getFirstDispatcher() {

@Override
public BasicEventDispatcher getLastDispatcher() {
if (systemMenuHandler != null) {
return systemMenuHandler;
}
return eventHandlerManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import java.util.List;

import com.sun.javafx.menu.MenuBase;

import javafx.scene.input.KeyEvent;

/**
* We use this interface to access the Glass native system menu
Expand All @@ -45,4 +45,5 @@ public interface TKSystemMenu {

public void setMenus(List<MenuBase> menus);

public void handleKeyEvent(KeyEvent event);
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ protected MenuBar getMenuBar() {
}
}

@Override
public void handleKeyEvent(javafx.scene.input.KeyEvent event) {
if (glassSystemMenuBar.handleKeyEvent(event.getCode().getCode(), glassModifiers(event))) {
event.consume();
}
}

// Clear the menu to prevent a memory leak, as outlined in JDK-8094232
private void clearMenu(Menu menu) {
ListChangeListener<MenuItemBase> lcl = menuListeners.get(menu);
Expand Down Expand Up @@ -407,4 +414,31 @@ private int glassModifiers(KeyCombination kcc) {
return (ret);
}

private int glassModifiers(javafx.scene.input.KeyEvent event) {
int ret = 0;
if (event.isShiftDown()) {
ret |= KeyEvent.MODIFIER_SHIFT;
}
if (event.isControlDown()) {
ret |= KeyEvent.MODIFIER_CONTROL;
}
if (event.isAltDown()) {
ret |= KeyEvent.MODIFIER_ALT;
}
if (event.isShortcutDown()) {
if (PlatformUtil.isMac()) {
ret |= KeyEvent.MODIFIER_COMMAND;
} else {
ret |= KeyEvent.MODIFIER_CONTROL;
}
}
if (event.isMetaDown()) {
if (PlatformUtil.isLinux()) {
ret |= KeyEvent.MODIFIER_WINDOWS;
} else if (PlatformUtil.isMac()) {
ret |= KeyEvent.MODIFIER_COMMAND;
}
}
return ret;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@

+ (void)registerKeyEvent:(NSEvent*)event;
+ (jint)getKeyCodeForChar:(jchar)c;
+ (void)setMenuKeyEvent:(NSEvent*)event;
+ (BOOL)handleMenuKeyEventForCode:(jint)code modifiers:(jint)modifiers;

+ (BOOL)syncRenderingDisabled;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
// embedded mode.
static NSString* awtEmbeddedEvent = @"AWTEmbeddedEvent";

static NSEvent* menuKeyEvent = nil;

#ifdef STATIC_BUILD
jint JNICALL JNI_OnLoad_glass(JavaVM *vm, void *reserved)
#else
Expand Down Expand Up @@ -908,6 +910,30 @@ + (jint)getKeyCodeForChar:(jchar)c;
}
}

+ (void)setMenuKeyEvent:(NSEvent*)event
{
if (menuKeyEvent != nil) {
[menuKeyEvent release];
menuKeyEvent = nil;
}
if (event != nil) {
menuKeyEvent = [event retain];
}
}

+ (BOOL)handleMenuKeyEventForCode:(jint)code modifiers:(jint)modifiers;
{
BOOL result = NO;
if (menuKeyEvent != nil) {
if (code == GetJavaKeyCode(menuKeyEvent) && modifiers == GetJavaKeyModifiers(menuKeyEvent)) {
result = [NSApp.mainMenu performKeyEquivalent: menuKeyEvent];
[menuKeyEvent release];
menuKeyEvent = nil;
}
}
return result;
}

+ (BOOL)syncRenderingDisabled {
return disableSyncRendering;
}
Expand Down
26 changes: 26 additions & 0 deletions modules/javafx.graphics/src/main/native-glass/mac/GlassMenu.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#import "GlassMenu.h"
#import "GlassHelper.h"
#import "GlassKey.h"
#import "GlassApplication.h"

#pragma clang diagnostic ignored "-Wdeprecated-declarations"

Expand Down Expand Up @@ -380,6 +381,31 @@ - (void)_setPixels:(jobject)pixels
GLASS_CHECK_EXCEPTION(env);
}

/*
* Class: com_sun_glass_ui_mac_MacMenuBarDelegate
* Method: _handleKeyEvent
* Signature: (JII)Z
*/
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacMenuBarDelegate__1handleKeyEvent
(JNIEnv *env, jobject jMenuDelegate, jlong jMenubarPtr, jint code, jint modifiers)
{
LOG("Java_com_sun_glass_ui_mac_MacMenuBarDelegate__1handleKeyEvent");

jboolean result = false;

GLASS_ASSERT_MAIN_JAVA_THREAD(env);
GLASS_POOL_ENTER;
{
if ([GlassApplication handleMenuKeyEventForCode: code modifiers: modifiers]) {
result = true;
}
}
GLASS_POOL_EXIT;
GLASS_CHECK_EXCEPTION(env);

return result;
}

/*
* Class: com_sun_glass_ui_mac_MacMenuDelegate
* Method: _initIDs
Expand Down
24 changes: 7 additions & 17 deletions modules/javafx.graphics/src/main/native-glass/mac/GlassView3D.m
Original file line number Diff line number Diff line change
Expand Up @@ -361,23 +361,13 @@ - (BOOL)performKeyEquivalent:(NSEvent *)theEvent
{
KEYLOG("performKeyEquivalent");

// JDK-8093711, JDK-8094601 Command-EQUALS and Command-DOT needs special casing on Mac
// as it is passed through as two calls to performKeyEquivalent, which in turn
// create extra KeyEvents.
//
// If the user presses Command-"=" on a US keyboard the OS will send that
// to performKeyEquivalent. If it isn't handled it will then send
// Command-"+". This allows a user to invoke Command-"+" without using
// the Shift key. The OS does this for any key where + is the shifted
// character above =. It does something similar with the period key;
// Command-"." leads to Escape for dismissing dialogs. Here we detect and
// ignore the second key event.
if (theEvent != NSApp.currentEvent && NSApp.currentEvent == lastKeyEvent) {
return YES;
}

BOOL result = [self handleKeyDown: theEvent];
return result;
// We can return YES here unconditionally. If the scene graph wants to
// invoke the system menu it will call handleKeyEvent in the
// MenuBarDelegate while the KeyEvent is being dispatched.
[GlassApplication setMenuKeyEvent: theEvent];
[self handleKeyDown: theEvent];
[GlassApplication setMenuKeyEvent: nil];
return YES;
}

- (BOOL)handleKeyDown:(NSEvent *)theEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,10 @@ public void setMenus(List<MenuBase> menus) {
this.menus = menus;
}

@Override
public void handleKeyEvent(KeyEvent keyEvent) {
}

// make menus accessible to unit tests
public List<MenuBase> getMenus() {
return menus;
Expand Down
Loading