或者您可以重写 MenuBar 皮肤。
Javafx 做出了选择(或者它是一个错误?)当 ALT 键被按下而不是释放时,焦点被赋予 MenuBar,这是 Eclipse、Netbeans、...中的标准行为。
此外,当按下或释放 ALT_GRAPH 键时,不应将焦点赋予 MenuBar。
这是我建议的补丁。
请注意,只有第一个差异是相关的,最后一个差异仅用于在无法访问代码时编译代码。
基本上我已经将“firstMenuRunnable”分成了 3 个函数
firstMenuRunnable 仅在按下 F10 键时使用
deselectOnKeyPressed 在 menuBar 具有焦点且 ALT 键为
按下
focusOnFirstMenuOnKeyReleased 在 menuBar 没有时使用
焦点和 ALT 键被释放
因此,可以有标准行为,允许使用 ALT 键的加速器而不被 MenuBar 获取焦点。
--- com/sun/javafx/scene/control/skin/MenuBarSkin.java in C:\Program Files (x86)\Java\jdk1.8.0_131\javafx-src.zip
+++ C:\Users\daniel\dev\xxx\Layout\src\com\stimulus\control\MenuBarSkin.java
@@ -372,12 +491,21 @@
scene.getAccelerators().put(acceleratorKeyCombo, firstMenuRunnable);
// put focus on the first menu when the alt key is pressed
+ scene.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
+ altDown = false;
+ });
scene.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
- if (e.isAltDown() && !e.isConsumed()) {
- firstMenuRunnable.run();
+ if (e.isAltDown() && !e.isConsumed() && e.getCode().equals(KeyCode.ALT)) {
+ deselectMenusOnKeyPressed.run();
+ altDown = true;
}
});
+ scene.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
+ if (altDown) {
+ focusOnFirstMenuOnKeyReleased.run();
+ }
});
+ });
ParentTraversalEngine engine = new ParentTraversalEngine(getSkinnable());
engine.addTraverseListener(this);
@@ -434,7 +453,50 @@
}
};
+ private boolean menuDeselectedOnKeyPressed = false;
+ Runnable deselectMenusOnKeyPressed = new Runnable() {
+ public void run() {
+ /*
+ ** check that this menubar's container has contents,
+ ** and that the first item is a MenuButton....
+ ** otherwise the transfer is off!
+ */
+ menuDeselectedOnKeyPressed = false;
+ if (container.getChildren().size() > 0) {
+ if (container.getChildren().get(0) instanceof MenuButton) {
+// container.getChildren().get(0).requestFocus();
+ if (focusedMenuIndex >= 0) {
+ unSelectMenus();
+ menuDeselectedOnKeyPressed = true;
+ }
+ }
+ }
+ }
+ };
+ Runnable focusOnFirstMenuOnKeyReleased = new Runnable() {
+ public void run() {
+ /*
+ ** check that this menubar's container has contents,
+ ** and that the first item is a MenuButton....
+ ** otherwise the transfer is off!
+ */
+ if (container.getChildren().size() > 0) {
+ if (container.getChildren().get(0) instanceof MenuButton) {
+// container.getChildren().get(0).requestFocus();
+ if (focusedMenuIndex == -1 && !menuDeselectedOnKeyPressed) {
+ unSelectMenus();
+ menuModeStart(0);
+ openMenuButton = ((MenuBarButton) container.getChildren().get(0));
+ openMenu = getSkinnable().getMenus().get(0);
+ openMenuButton.setHover();
+ }
+ }
+ }
+ }
+ };
+
private boolean pendingDismiss = false;
// For testing purpose only.
@@ -650,9 +712,23 @@
menuButton.textProperty().bind(menu.textProperty());
menuButton.graphicProperty().bind(menu.graphicProperty());
menuButton.styleProperty().bind(menu.styleProperty());
+ // patch because MenuButtonSkin.AUTOHIDE is private
+ final String AUTOHIDE;
+ {
+ try {
+ Class<?> clazz = MenuButtonSkin.class;
+// System.out.println("fields = " + Arrays.asList(clazz.getDeclaredFields()).toString());
+ Field field = clazz.getDeclaredField("AUTOHIDE");
+ field.setAccessible(true);
+ AUTOHIDE = (String) field.get(this);
+ field.setAccessible(false);
+ } catch (NoSuchFieldException | SecurityException | IllegalAccessException | IllegalArgumentException ex) {
+ throw new UnsupportedOperationException(ex);
+ }
+ }
menuButton.getProperties().addListener((MapChangeListener<Object, Object>) c -> {
- if (c.wasAdded() && MenuButtonSkin.AUTOHIDE.equals(c.getKey())) {
- menuButton.getProperties().remove(MenuButtonSkin.AUTOHIDE);
+ if (c.wasAdded() && AUTOHIDE.equals(c.getKey())) {
+ menuButton.getProperties().remove(AUTOHIDE);
menu.hide();
}
});
下面是我的 MenuBar 的完整代码:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.stimulus.control;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Skin;
/**
*
* @author daniel
*/
public class CustomMenuBar extends MenuBar {
public CustomMenuBar() {
}
public CustomMenuBar(Menu... menus) {
super(menus);
}
@Override
protected Skin<?> createDefaultSkin() {
return new MenuBarSkin(this) {
};
}
}