简体   繁体   中英

How to hide Swing accelerators by a text pane?

I have a Swing application with multiple panes. Some of the panes are text ones (JTextPane), some are dialog-like (with buttons and sliders) and some are graphical (custom painted ). I have a few actions defined in the main menu with simple accelerators like K , P or O .

I would like those accelerators to be processed by the menu actions only if the currently focused pane is not processing them. Specifically, I do not want them to be processed by the menu when the user is just typing in a text pane.

I am creating actions and menu items using:

action = new javax.swing.AbstractAction
new MenuItem(action)

I am registering accelerators with:

action.putValue(javax.swing.Action.ACCELERATOR_KEY, keyStroke)

Is it possible to "eat" (suppress) the key press event for the keys which are processed in the text panes so that they are not passed to the main menu for the global processing?

If not, are there some alternatives to do something similar, like to register the accelerators I know should not be processed when in a text pane for some panes only?

I am adding a code based on an answer to make the question clearer (and to make developing alternate solutions easier):

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        final Action oAction = new PrintAction("O",KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
        menu.add(oAction);
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);
        JTextArea area = new JTextArea("Here I want no accelerators", 10, 40);
        frm.add(new JScrollPane(area));
        frm.add(new JTextField("Here I want accelerators working"), BorderLayout.SOUTH);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
            putValue(Action.ACCELERATOR_KEY, aMnemonic);
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}

Here is the example. In text area no key bindings working. In text field work all key bindings. Also all the menu items are accessible (enabled) from the menu.

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        menu.add(new PrintAction("O", KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)));
        menu.add(new PrintAction("K", KeyStroke.getKeyStroke(KeyEvent.VK_K, 0)));
        menu.add(new PrintAction("P", KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)));
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);
        JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
        area.addFocusListener(new FocusListener() {

            @Override
            public void focusLost(FocusEvent e) {
                setItemStatus(menu, true);
            }

            @Override
            public void focusGained(FocusEvent e) {
                setItemStatus(menu, false);
            }
        });
        frm.add(new JScrollPane(area));
        frm.add(new JTextField("Here working accelerators"), BorderLayout.SOUTH);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static void setItemStatus(JMenu aMenu, boolean aStatus) {
        for (Component item : aMenu.getMenuComponents()) {
            ((JMenuItem) item).getAction().setEnabled(aStatus);
        }
    }
    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
            putValue(Action.ACCELERATOR_KEY, aMnemonic);
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}

Here is a solution using KeyBindinds, as suggested by camickr . It is shorter than the one provided by Sergiy Medvynskyy , and I find it more straightforward, but it has a drawback the shortcut is not displayed in the menu, which is a result of the shortcut not being defined in the action itself.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        final Action oAction = new PrintAction("O");
        menu.add(oAction);
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);
        JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
        frm.add(new JScrollPane(area));
        frm.add(new JTextField("Here working accelerators") {
            {
                getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0), "command_O");
                getActionMap().put("command_O", oAction);
            }
        }, BorderLayout.SOUTH);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}

It is possible to use KeyEventDispatcher to filter key events.

(Credit: I have adapted the code from answer to Application wide keyboard shortcut )

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        final Action oAction = new PrintAction("O",KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
        menu.add(oAction);
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);

        final JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
        frm.add(new JScrollPane(area));

        frm.add(new JTextField("Here working accelerators"), BorderLayout.SOUTH);

        KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        kfm.addKeyEventDispatcher( new KeyEventDispatcher() {
            @Override
            public boolean dispatchKeyEvent(KeyEvent e) {
                KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
                // pass only KEY_TYPED for letters with no modifiers in the editing area, suppress KEY_PRESSED, KEY_RELEASED
                return area.isFocusOwner() && keyStroke.getModifiers()==0 && e.getID()!=KeyEvent.KEY_TYPED && Character.isLetter(e.getKeyChar());
            }
        });

        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
            putValue(Action.ACCELERATOR_KEY, aMnemonic);
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM