单击 JFXPanel 中嵌入的打开 JavaFX 菜单不会将其关闭

Click on open JavaFX Menu embedded in JFXPanel does not close it

通过 JFXPanel 在 Swing window 中嵌入菜单时,我无法通过单击关闭菜单。有时它会闪烁,好像它关闭并立即重新打开。

package testjavafx;

public class TestMenuJavaFX extends Application {

    @Override
    public void start(Stage primaryStage) {
        MenuBar menuBar = new MenuBar(
                new Menu("Menu 1", null,
                        new MenuItem("Menu item 1-1"),
                        new MenuItem("Menu item 1-2")),
                new Menu("Menu 2", null,
                        new MenuItem("Menu item 2-1"),
                        new MenuItem("Menu item 2-2")),
                new Menu("Menu 3", null,
                        new MenuItem("Menu item 3-1"),
                        new MenuItem("Menu item 3-1")));
        menuBar.setPrefWidth(300);
        Region root = new Pane(menuBar);
        root.setPrefSize(300, 185);

        useJFXPanel(root);
        //usePrimaryStage(primaryStage, root);
    }

    private static void useJFXPanel(Region root) {
        JFXPanel jfxPanel = new JFXPanel();
        jfxPanel.setScene(new Scene(root));
        JFrame jFrame = new JFrame("test menu JavaFX");
        jFrame.setSize((int) root.getWidth(), (int) root.getHeight());
        jFrame.add(jfxPanel);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setLocationRelativeTo(null);
        jFrame.setVisible(true);
    }

    private static void usePrimaryStage(Stage primaryStage, Parent root) {
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(TestMenuJavaFX.class, args);
    }
}

通过使用方法 usePrimaryStage 我得到了预期的行为(点击菜单打开它,再次点击关闭它),但是 useJFXPanel 问题出现了。

这是一个事件处理问题,鼠标单击首先作为 Swing 鼠标事件分派到 JFXPanel,然后 JFXPanel 在内部将 JavaFX 鼠标事件分派到其嵌入的 Scene
看起来,在 Swing 部分,菜单失去焦点并关闭,当事件到达 Menu 实例时,它发现它关闭并因此打开它。

我试图继承Menuclass来给它添加一个鼠标点击事件处理程序,但是它不处理鼠标点击,并且使用showing/shown和hiding/hidden 提供的事件对它没有帮助(因为问题发生得更早)。
我还尝试通过 subclass MenuBar 添加鼠标单击事件处理程序,但只有在菜单外的栏上单击时才会调用该处理程序,所以这里没有运气,而 subclass JFXPanel 覆盖 processMouseEvent 并通过反射黑魔法检索 MenuBarButton 实例,但我无法使其工作。

这是一个错误,对吧?是否有解决此问题的(简单干净,理想情况下)解决方法?

我正在使用 OpenJDK 11.0.10.9 和 JavaFX 17.0.0.1。

在我的系统上,这不会 运行,但会在启动时挂起,因为 JFrame 是在错误的线程上创建和显示的。更正确实显示了您描述的行为,这似乎是一个错误。

我找到了一个解决方法,即捕获一个 ON_HIDING 事件并使用 Platform.runLater(...) 安排调用以隐藏事件队列中更靠后的菜单。生成的代码如下所示;

public class TestMenuJavaFX extends Application {

    @Override
    public void start(Stage primaryStage) {
        MenuBar menuBar = new MenuBar(
                new Menu("Menu 1", null,
                        new MenuItem("Menu item 1-1"),
                        new MenuItem("Menu item 1-2")),
                new Menu("Menu 2", null,
                        new MenuItem("Menu item 2-1"),
                        new MenuItem("Menu item 2-2")),
                new Menu("Menu 3", null,
                        new MenuItem("Menu item 3-1"),
                        new MenuItem("Menu item 3-1")));
        menuBar.setPrefWidth(300);
        
        menuBar.getMenus().forEach(menu -> {
            menu.addEventHandler(Menu.ON_HIDING, e -> {
                Platform.runLater(menu::hide);
            });
        });
        
        Region root = new Pane(menuBar);
        root.setPrefSize(300, 185);

        useJFXPanel(root);
        //usePrimaryStage(primaryStage, root);
    }

    private static void useJFXPanel(Region root) {
        JFXPanel jfxPanel = new JFXPanel();
        jfxPanel.setScene(new Scene(root));
        SwingUtilities.invokeLater(() -> {
            JFrame jFrame = new JFrame("test menu JavaFX");
            jFrame.setSize((int) root.getWidth(), (int) root.getHeight());
            jFrame.add(jfxPanel);
            jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jFrame.setLocationRelativeTo(null);
            jFrame.setVisible(true);
        });
    }

    private static void usePrimaryStage(Stage primaryStage, Parent root) {
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(TestMenuJavaFX.class, args);
    }
}