Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

JTextField consuming enter and escape

584 views
Skip to first unread message

Jason Siemens

unread,
Jan 14, 2004, 10:59:26 AM1/14/04
to

Does anyone know a way to stop a JTextField component from consuming
VK_ENTER and VK_ESCAPE keys? I need these keys to be picked up by the
parent to drive an OK and Cancel button (it works fine for the other
controls on the same panel).

I have of course tried all of the following (picked up from various
sites, etc. around the web) when the control is created, with no luck:

{
KeyStroke en = KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0, false );
KeyStroke esc = KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0, false );

unregisterKeyboardAction( en );
unregisterKeyboardAction( esc );

getKeymap().removeKeyStrokeBinding( en );
getKeymap().removeKeyStrokeBinding( esc );

getInputMap( JComponent.WHEN_FOCUSED ).remove( en );
getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ).remove( en );
getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).remove(
en );

getInputMap( JComponent.WHEN_FOCUSED ).remove( esc );
getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ).remove( esc );
getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).remove(
esc );
}

{
KeyStroke en = KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0, true );
KeyStroke esc = KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0, true );

unregisterKeyboardAction( en );
unregisterKeyboardAction( esc );

getKeymap().removeKeyStrokeBinding( en );
getKeymap().removeKeyStrokeBinding( esc );

getInputMap( JComponent.WHEN_FOCUSED ).remove( en );
getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ).remove( en );
getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).remove(
en );

getInputMap( JComponent.WHEN_FOCUSED ).remove( esc );
getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ).remove( esc );
getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).remove(
esc );
}

Thanks,

Jason.

Jim Sculley

unread,
Jan 14, 2004, 12:30:59 PM1/14/04
to
Jason Siemens wrote:

>
> Does anyone know a way to stop a JTextField component from consuming
> VK_ENTER and VK_ESCAPE keys? I need these keys to be picked up by the
> parent to drive an OK and Cancel button (it works fine for the other
> controls on the same panel).
>
> I have of course tried all of the following (picked up from various
> sites, etc. around the web) when the control is created, with no luck:

You are missing the relationship between action maps and input maps.
The InputMap ties a particular key sequence to an action identifier.
This action identifier is used to look up the appropriate action in the
ActionMap. Making changes to the InputMap won't affect the ActionMap if
the specified action identifier doesn't exist. By default, there is no
action assigned to ENTER or ESC in the ActionMap of a JTextField. As a
result, adding/reassigning items to/in the InputMap for ENTER or ESC
will have no affect on the application.

Here's a small example which prints a message to stdout when the ENTER
or ESC is pressed while in a JTextField:

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

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

public class TFTest {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextField tf = new JTextField(20);

InputMap im = tf.getInputMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape");

ActionMap am = tf.getActionMap();
am.put("enter", new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
System.out.println("ENTER pressed");
}
});

am.put("escape", new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
System.out.println("ESC pressed");
}
});

f.getContentPane().add(tf);
f.pack();
f.setVisible(true);
}
}


Jim S.
--
Remove my extraneous mandibular appendages to reply via e-mail

Jason Siemens

unread,
Jan 14, 2004, 5:26:39 PM1/14/04
to
Thanks Jim,

You are correct, the action/input maps were not doing anything. I was
able to capture the Enter and Escape events using your method. What I
really want to achieve is to have the JTextField control act the same as
any other control such that I can capture the VK_ENTER and VK_ESCAPE on
the root pane on the dialog.

To elaborate, I have subclassed both JTextField and JDialog. In
JDialog, I have code in the JDialog subclass to click the cancel button
in the dialog whenever Escape is pressed (and the default button when
the Enter is pressed):

getRootPane().registerKeyboardAction(
new ButtonClickAction( cancelButton ),
KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ),
JComponent.WHEN_IN_FOCUSED_WINDOW );

This works when any control has the focus, whether it be JPasswordField,
JTextBox, JButton, etc.. Everything except JTextField.

I did try your method of adding an Action to the ActionMap for the
JTextfield. This would work fine if I could figure out how to send a
KeyEvent directly to the parent without it coming back to the JTextField
(and creating a stack overflow in the process).

Jason.

Alan Moore

unread,
Jan 15, 2004, 5:00:06 AM1/15/04
to
On Wed, 14 Jan 2004 17:26:39 -0500, Jason Siemens
<jason....@namsys.com> wrote:

>To elaborate, I have subclassed both JTextField and JDialog. In
>JDialog, I have code in the JDialog subclass to click the cancel button
>in the dialog whenever Escape is pressed (and the default button when
>the Enter is pressed):
>
>getRootPane().registerKeyboardAction(
> new ButtonClickAction( cancelButton ),
> KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ),
> JComponent.WHEN_IN_FOCUSED_WINDOW );
>

You have an action that clicks a button? That's very, um, creative!

Here's how I do it. To have the ESC key close the dialog, I use this
method:

protected void dialogInit()
{
super.dialogInit();
JLayeredPane layeredPane = getLayeredPane();
closeDialogAction = getCloseDialogAction();
String actionName = "close-dialog";
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
layeredPane.getActionMap().put(actionName, closeDialogAction);
layeredPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(stroke, actionName);
}

When I create the "Cancel" button, I attach the same action to it:

cancelButton = new JButton(dialog.getCloseDialogAction());

In my case, the button's label is taken from the action's properties,
but you can also set it yourself:

cancelButton.setText("Cancel");


As for the ENTER key, all you should have to do is set the
corresponding button as the RootPane:

dialog.getRootPane().setDefaultButton(okButton);

However, if you add an ActionListener to your JTextField, it will
consume the ENTER key, as that's what triggers the ActionLisetner.
Otherwise, as Jim said, JTextField will ignore the ENTER key.

Jason Siemens

unread,
Jan 15, 2004, 10:58:00 AM1/15/04
to

Thanks Alan,

I found out what my problem was...my own stupidity. Last week, I
changed my subclass of JTextField to inherit from JFormattedTextField
instead and completely forgot I did it. Of course, this is a completely
different component and this problem is a known bug in 1.4.1 (Bug parade
#4741926).

So, I got around it by adding an action handler (for both Enter and
Escape) to my dialog like this :

JRootPane rootPane = getRootPane( );
KeyStroke ky = KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 );
rootPane.getActionMap( )
.put( InterfaceConstants.DEFAULT_BUTTON_ACTION_NAME,
new ButtonClickAction( defaultButton ) );
rootPane.getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW )
.put( ky, InterfaceConstants.DEFAULT_BUTTON_ACTION_NAME );

And then adding a KeyListener to the formatted text field that calls the
action attached to the root pane directly:

public void keyPressed( KeyEvent e ) {
int key = e.getKeyCode( );

if ( key == KeyEvent.VK_ESCAPE ) {
/* call the cancel button action */
Action cancelAction = getRootPane( )
.getActionMap( )
.get(
InterfaceConstants.CANCEL_BUTTON_ACTION_NAME );

if ( cancelAction != null ) {
cancelAction.actionPerformed( null );
}
} else if ( key == KeyEvent.VK_ENTER ) {
/* call the default button action */
Action defaultAction = getRootPane( )
.getActionMap( )
.get(
InterfaceConstants.DEFAULT_BUTTON_ACTION_NAME );

if ( defaultAction != null ) {
defaultAction.actionPerformed( null );
}
}
}

Thanks for your help.

0 new messages