【问题标题】:How to disable mnemonic for JavaFX MenuBar?如何禁用 JavaFX MenuBar 的助记符?
【发布时间】:2016-06-30 19:33:44
【问题描述】:

在我的舞台上,我像往常一样在顶部插入了一个菜单栏。我想在舞台的另一个上下文中给 ALT 键(连同箭头键)一些逻辑。但每次我按 ALT 和箭头时,我也会无意中浏览菜单栏的菜单。

我想避免这种情况,或者更好地完全禁用这种助记行为。 将所有菜单的 mnemonicParsing 属性设置为 false 失败。我也尝试过这种方法但没有成功:

menubar.addEventFilter(KeyEvent.ANY, e -> e.consume());

【问题讨论】:

    标签: javafx menubar keyevent mnemonics alt-key


    【解决方案1】:

    ALT 被按下时,第一个菜单获得焦点,当菜单获得焦点时,无论是否按下 ALT,箭头键都会导致它们之间的导航。因此,为了防止这种行为,您需要在按下 ALT 时防止第一个菜单获得焦点。

    查看MenuBarSkin类的构造函数源码,给出了解决方案:

    public MenuBarSkin(final MenuBar control) {
        ...
        Utils.executeOnceWhenPropertyIsNonNull(control.sceneProperty(), (Scene scene) -> {
            scene.getAccelerators().put(acceleratorKeyCombo, firstMenuRunnable);
    
            // put focus on the first menu when the alt key is pressed
            scene.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
                if (e.isAltDown()  && !e.isConsumed()) {
                    firstMenuRunnable.run();
                }
            });
        });
        ...
    }
    

    解决方案:

    正如您已经猜到的,解决方案是在 ALT 关闭时使用该事件,但您需要将 EventHandler 添加到 scene 而不是 menubar

    scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent event) {
            // your desired behavior
            if(event.isAltDown())
                event.consume();
        }
    });
    

    【讨论】:

    • 到处都是很棒的答案,您甚至查找了注册关键侦听器的原始来源。谢谢你,你应该得到赏金。
    【解决方案2】:

    或者您可以重写 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) {
    
            };
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多