简体   繁体   中英

Non-editable JEditorPane consumes Enter key

I have an JDialog containing a JEditorPane for showing non-user-editable HTML content, such as Help and Release Notes.

The JDialog has a "Close" button that is installed as the default button.

If the JEditorPane is left "focusable", then the Page Up/Down keys will scroll through the document, but pressing "Enter" does not fire the default button.

On the other hand, if the JEditorPane is set non-focusable, then Page Up/Down keys do not work, but pressing the "Enter" key does fire the default button, closing the dialog.

@SuppressWarnings("serial")
public class NoteViewer extends JFrame {

  public static void main(String[] args) throws IOException {
    final NoteViewer viewer = new NoteViewer(new URL("http://example.com/"));
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            viewer.setVisible(true);
        }
    });
  }

  public NoteViewer(URL url) throws IOException {
    super("Note Viewer");
    setSize(900, 200);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JEditorPane notes = new JEditorPane(url);
    notes.setEditable(false);
    // notes.setFocusable(false);

    getContentPane().add(new JScrollPane(notes), BorderLayout.CENTER);

    JButton close = new JButton("Close");
    close.addActionListener(EventHandler.create(ActionListener.class, this, "dispose"));
    Box box = Box.createHorizontalBox();
    box.add(Box.createHorizontalGlue());
    box.add(close);
    getContentPane().add(box, BorderLayout.PAGE_END);

    getRootPane().setDefaultButton(close);
  }
}

Uncomment the notes.setFocusable(false) line to see the different behavior.

I would like the JEditorPane to process the navigation keystrokes (such as Page Up/Down), but ignore (not consume) the "editing" keystrokes, such as Enter, so that the Root Pane will invoke the default button.

After much hacking and single-stepping, I've got the behaviour I'm looking for with this code:

notes.getInputMap().put(
    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
    "pressed");

but I am concerned that it is rather fragile. Do all platforms use VK_ENTER to invoke the default button? Do all platforms use "pressed" as the command-string to invoke the default button?

Finally, it is going about it the wrong way: instead of the JEditorPane ignoring the Enter key and letting the processing happen in an ancestor, this is the JEditorPane explicitly handling the enter key, which strikes me as wrong.

A non-editable JEditorPane should not capture all the editing keystrokes (AZ, 0-9, Enter, Delete, etc.) and transform them into a warning beep, but rather leave them unhandled so that parent components get a chance. How can this be achieved in a general, non keystroke-by-keystroke input map fashion?

Do all platforms use "pressed" as the command-string to invoke the default button?

That is not what is happening.

You are mapping the Enter key to the "pressed" Action of the JEditorPane. However, there is no "pressed" Action so the Binding is ignored and the event is passed up to the root pane where the Enter key binding for the default button is used.

Normally "none" is used to indicate you want to ignore/remove the binding. Check out the section from the Swing tutorial on How to Remove Bindings for more information.

So I would say you solution is correct and should work on all platforms.

You may want to check out Key Bindings for a programs that displays all the key bindings for each component. You will see that there is no "pressed" action. JEditorPane actually uses "insert-break" to map to the Action.

A non-editable JEditorPane should not capture all the editing keystrokes (AZ, 0-9, Enter, Delete, etc.) and transform them into a warning beep,

I don't have a "beeping" problem with az, 0-9. I do have a problem with the delete and backspace keys.

I'm using JDK8_45 on Windows 7.

Maybe you can prevent the dispatching of keys by using a KeyEventDispatcher . Maybe you check the source of the KeyEvent and if the source is the editor pane you only let the Enter key through? Might also need to allow the PageUp/PageDown events so scrolling will work.

Check out Global Event Dispatching for more information.

I think I just found a better way: set the JEditorPane as not editable and not focusable, and the JScrollPane as focusable.

JEditorPane notes = new JEditorPane(url);
notes.setEditable(false);
notes.setFocusable(false);

JScrollPane scroller = new JScrollPane(notes);
scroller.setFocusable(true);
getContentPane().add(scroller, BorderLayout.CENTER);

The Enter key is forwarded to the default button. Backspace, delete and friends don't generate any beeps.

Edit: Doesn't allow selecting and copying of any text, so perhaps not the best solution.

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