无法通过将鼠标移开来取消 JavaFX MenuItem

标签 java javafx

enter image description here

如果您按住鼠标单击其中一个 MenuItem,然后拖动鼠标离开,该按钮将保持选中状态。即使将鼠标拖动到 NewSaveOpen 按钮仍将保持选中状态。如果您在 Open 之外的任何地方释放鼠标,该命令将仍然执行。换句话说,在您按住鼠标的那一刻,即将调用 OpenonAction()

这与 Mac OSX 中的正常行为不同,我猜也与 Windows native 应用程序不同。在它们中,即使您按住菜单项上的点击,如果您将鼠标移开,按钮也不会触发。但它确实发生在 JavaFX 中。

我该怎么做才能解决这个问题? JavaFX 8。

最佳答案

免责声明:这不是一个合适的解决方案,它很丑陋,没有经过充分测试,并且有很多限制(见下文)。它也可能会随着下一个 Java 版本的发布而中断,但是它解决了我非常标准的普通 Vanilla 案例中的问题,所以我决定分享它以防万一有人发现它有用。使用它你自己的风险! :)

我也希望对 JavaFX 皮肤 API 有更好了解的人可以改进它,我将不胜感激!

限制:

  • 仅适用于 ContextMenu,不适用于主菜单(这是我的用例)
  • 仅适用于一级菜单(不知道如何扩展子菜单)
  • 不确定它是否与 CustomMenuItem 一起使用,或者与带有 GraphicMenuItem 一起使用,但它可能!
  • 它依赖于标准的 ContextMenuContent 作为 ContextMenu 的外观。如果您有自己的皮肤,那么它将不起作用。

这是辅助类:

public class ContextMenuFixer {

public static void fix(ContextMenu contextMenu) {
    if (contextMenu.getSkin() != null) {
        fix(contextMenu, (ContextMenuContent) contextMenu.getSkin().getNode());
    } else {
        contextMenu.skinProperty().addListener((observable, oldValue, newValue) -> {
            if(newValue != null) {
                fix(contextMenu, (ContextMenuContent) contextMenu.getSkin().getNode());
            }
        });
    }
}

private static void fix(ContextMenu menu, ContextMenuContent content) {

    content.getItemsContainer().getChildren().forEach(node -> {

        EventHandler<? super MouseEvent> releaseEventFilter = event -> {
            if (!((Node) event.getTarget()).isFocused()) {
                event.consume();
                menu.hide();
            }
        };
        node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
            node.removeEventFilter(MouseEvent.MOUSE_RELEASED, releaseEventFilter);

        });

        node.addEventHandler(MouseEvent.DRAG_DETECTED, event -> {
            node.startFullDrag();
            node.addEventFilter(MouseEvent.MOUSE_RELEASED, releaseEventFilter);
        });

        node.addEventHandler(MouseDragEvent.MOUSE_DRAG_ENTERED, event -> {
            MouseEvent e = event.copyFor(event.getSource(), event.getTarget(), MouseEvent.MOUSE_ENTERED);
            node.fireEvent(e);
        });

        node.addEventHandler(MouseDragEvent.MOUSE_DRAG_RELEASED, event -> {
            Event e = event.copyFor(event.getSource(), event.getTarget(), MouseEvent.MOUSE_RELEASED);
            node.fireEvent(e);
        });

        node.addEventHandler(MouseDragEvent.MOUSE_DRAG_EXITED, event -> {
            node.getParent().requestFocus();
        });

        node.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> {
            menu.hide();
        });

    });
}

用法非常简单:

    ContextMenuFixer.fix(myContextMenu);

附言一个更简洁的解决方案是编写您自己的适当皮肤类(即替换 ContextMenuContent),但由于维护成本,我故意不想这样做。

关于无法通过将鼠标移开来取消 JavaFX MenuItem,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29283426/

相关文章:

java - 在服务器端 java、ios 和 android 之间共享翻译

java - 从字符串中删除相邻重复项,仅保留一个实例 : java

java - insert into 命令中存在语法错误。输入日期值时可能出现错误

javafx - 如何在javafx 2.1中将节点转换为图像?

java - 慢慢减少数量

java - 当多个客户端查看相同数据时,如何使有状态 Web 客户端保持同步?

java - JavaFX 应用程序中 "<release>"的 "<configuration>"元素是什么

java - 桌面应用程序的 Swing 与 JavaFx

java - 我无法在 JavaFX 中更改 ScrollPane 的 Angular 颜色

java - 使用数组时 ColumnConstraints.setPercentWidth() 不起作用