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

时间:2010-09-28 09:45:35

标签: java jdialog keyevent jpopupmenu

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

2 个答案:

答案 0 :(得分:4)

寻找通用解决方案有点挑战。我们需要考虑何时:

  1. 使用轻量级弹出
  2. 使用重量级弹出
  3. 我确定在两种情况下,当按下转义键时,根窗格实际上具有焦点。

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

    在第二种情况下,创建一个Window以包含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课程。

答案 1 :(得分:0)

我也遇到了问题。 @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 ) );
      }
    } );