如果基础窗口在 ESC 上关闭,如何为 JPopupMenu 启用 ESC-Close?

发布于 2024-09-24 21:24:02 字数 274 浏览 3 评论 0原文

想象一下两种常见情况的结合:一个 JDialog(或 JFrame)在 VK_ESCAPE(设置为根窗格上的键绑定)上关闭,而内部 JPopupMenu 也应该在 ESC 上关闭。问题是:如果弹出窗口可见,按 escape 总是会关闭对话框 - 事件。显然,弹出窗口甚至没有接收到按键事件,因此弹出窗口无法使用它。有什么方法可以使其正常工作,以便在第一个 ESC 事件时关闭弹出窗口,在第二个事件时关闭对话框? 顺便说一句:它确实可以与 JComboBox 一起使用,默认情况下,当按下 escape 时 JComboBox 会关闭。

Imagine two common situations combined: A JDialog(or JFrame) which closes on VK_ESCAPE (set as key binding on the root pane) and an inner JPopupMenu which is supposed to close on ESC as well. The problem is: pressing escape always closes the dialog - event if the popup is visible. Apparently the popup doesn't even receive the key event so it can't be consumed by the popup. Is there any way to get this working correctly, so that on the first ESC-event the popup is closed and on the second the dialog closes?
By the way: It does work with a JComboBox, which by default closes when escape is pressed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

美人如玉 2024-10-01 21:24:02

找到一个通用的解决方案是一个挑战。我们需要考虑何时:

  1. 使用轻量级弹出窗口 使用
  2. 重量级弹出窗口

我确定在这两种情况下,当按下转义键时,根窗格实际上具有焦点。

在第一种情况下,我只是搜索根窗格以查看 JPopupMenu 是否已添加到 GUI。如果是这样,那么我们可以关闭弹出窗口。

在第二种情况下,创建一个窗口来包含 JPopupMenu,因此我进行搜索以查看是否显示可见的自定义弹出窗口。如果是这样,那么我就把窗户处理掉。

如果上述两种情况都不成立,那么您可以直接关闭对话框。

import java.awt.*;
import java.awt.event.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;

public class DialogEscape extends JDialog
{
    private JPopupMenu popup;

    public DialogEscape()
    {
        popup = new JPopupMenu();
        popup.add( new JMenuItem("SubMenuA") );
        popup.add( new JMenuItem("SubMenuB") );
        popup.add( new JMenuItem("SubMenuC") );
        popup.add( new JMenuItem("SubMenuD") );

        String[] items = { "Select Item", "Color", "Shape", "Fruit" };
        JComboBox comboBox = new JComboBox( items );
        add(comboBox, BorderLayout.NORTH);

        JTextField textField = new JTextField("Right Click For Popup");
        textField.setComponentPopupMenu(popup);
        add(textField);

        KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action escapeAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                boolean openPopup = false;
                Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

                //  Check if light weight popup is being used

                List<JPopupMenu> popups = SwingUtils.getDescendantsOfType(JPopupMenu.class, (Container)c, true);

                for (JPopupMenu p: popups)
                {
                    p.setVisible( false );
                    openPopup = true;
                }

                //  Check if a heavy weight popup is being used

                Window window = SwingUtilities.windowForComponent(c);
                Window[] windows = window.getOwnedWindows();

                for (Window w: windows)
                {
                    if (w.isVisible()
                    &&  w.getClass().getName().endsWith("HeavyWeightWindow"))
                    {
                        openPopup = true;
                        w.dispose();
                    }
                }

                //  No popups so close the Window

                if (! openPopup)
//                  SwingUtilities.windowForComponent(c).setVisible(false);
                    SwingUtilities.windowForComponent(c).dispose();
            }
        };

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE");
        getRootPane().getActionMap().put("ESCAPE", escapeAction);
    }

    public static void main(String[] args)
    {
        String laf = null;
        laf = "javax.swing.plaf.metal.MetalLookAndFeel";
//      laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
//      laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";

        try { UIManager.setLookAndFeel(laf); }
        catch (Exception e2) { System.out.println(e2); }

        JDialog dialog = new DialogEscape();
        dialog.setDefaultCloseOperation( HIDE_ON_CLOSE );
        dialog.setSize(200, 200);
        dialog.setLocationRelativeTo(null);
        dialog.setVisible( true );
    }
}

您还需要下载 Swing Utils 类。

Finding a generic solution was a bit of a challenge. We need to consider when:

  1. a light weight popup is used
  2. a heavy weight popup is used

I determined that in both cases the root pane actually has focus when the escape key is pressed.

In the first case I just search the root pane to see if a JPopupMenu has been added to the GUI. If so, then we can just close the popup.

In the second case a Window is created to contain the JPopupMenu so I do a search to see if a visible custom popup Window is displayed. If so, then I dispose of the window.

If neither of the above two cases is true then you can just close the dialog.

import java.awt.*;
import java.awt.event.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;

public class DialogEscape extends JDialog
{
    private JPopupMenu popup;

    public DialogEscape()
    {
        popup = new JPopupMenu();
        popup.add( new JMenuItem("SubMenuA") );
        popup.add( new JMenuItem("SubMenuB") );
        popup.add( new JMenuItem("SubMenuC") );
        popup.add( new JMenuItem("SubMenuD") );

        String[] items = { "Select Item", "Color", "Shape", "Fruit" };
        JComboBox comboBox = new JComboBox( items );
        add(comboBox, BorderLayout.NORTH);

        JTextField textField = new JTextField("Right Click For Popup");
        textField.setComponentPopupMenu(popup);
        add(textField);

        KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action escapeAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                boolean openPopup = false;
                Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

                //  Check if light weight popup is being used

                List<JPopupMenu> popups = SwingUtils.getDescendantsOfType(JPopupMenu.class, (Container)c, true);

                for (JPopupMenu p: popups)
                {
                    p.setVisible( false );
                    openPopup = true;
                }

                //  Check if a heavy weight popup is being used

                Window window = SwingUtilities.windowForComponent(c);
                Window[] windows = window.getOwnedWindows();

                for (Window w: windows)
                {
                    if (w.isVisible()
                    &&  w.getClass().getName().endsWith("HeavyWeightWindow"))
                    {
                        openPopup = true;
                        w.dispose();
                    }
                }

                //  No popups so close the Window

                if (! openPopup)
//                  SwingUtilities.windowForComponent(c).setVisible(false);
                    SwingUtilities.windowForComponent(c).dispose();
            }
        };

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE");
        getRootPane().getActionMap().put("ESCAPE", escapeAction);
    }

    public static void main(String[] args)
    {
        String laf = null;
        laf = "javax.swing.plaf.metal.MetalLookAndFeel";
//      laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
//      laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";

        try { UIManager.setLookAndFeel(laf); }
        catch (Exception e2) { System.out.println(e2); }

        JDialog dialog = new DialogEscape();
        dialog.setDefaultCloseOperation( HIDE_ON_CLOSE );
        dialog.setSize(200, 200);
        dialog.setLocationRelativeTo(null);
        dialog.setVisible( true );
    }
}

You will also need to download the Swing Utils class.

这样的小城市 2024-10-01 21:24:02

我也遇到了这个问题。 @camickr 提供的解决方案对我来说似乎不太好。所以我检查了引擎盖下发生了什么。打开弹出窗口时,作为弹出窗口调用者的组件将失去焦点。焦点转到 JDialog 的 JRootPane,然后委托给弹出窗口。因此,我没有在 JRootPane 上注册“ESC”快捷方式,而是将其放在 ContentPane 上。这样,只有当 JRootPane 以外的任何内容获得焦点时,对话框才会在 ESC 上关闭。

    final JComponent contentPane = (JComponent) dialog.getContentPane();
    contentPane.getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ).put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), "EXIT" );
    contentPane.getActionMap().put( "EXIT", new AbstractAction()
    {
      @Override
      public void actionPerformed( final ActionEvent e )
      {
        dialog.dispatchEvent( new WindowEvent( dialog, WindowEvent.WINDOW_CLOSING ) );
      }
    } );

I encountered the problem as well. The solution provided by @camickr didn't seem very nice to me. So I checked out what's going on under the hood. When opening a popup, the component that is the invoker of the popup loses focus. The focus goes over to the JRootPane of the JDialog which then delegates to the popup. So instead of registering the "ESC" shortcut on the JRootPane, i simply put it on the ContentPane. This way, the dialog only closes on ESC when anything other than the JRootPane is focused.

    final JComponent contentPane = (JComponent) dialog.getContentPane();
    contentPane.getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ).put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), "EXIT" );
    contentPane.getActionMap().put( "EXIT", new AbstractAction()
    {
      @Override
      public void actionPerformed( final ActionEvent e )
      {
        dialog.dispatchEvent( new WindowEvent( dialog, WindowEvent.WINDOW_CLOSING ) );
      }
    } );
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文