I've designed (using the GUI designer within Netbeans) a small dialog with two radio buttons and a number spinner.
If I press Enter while the focus is on one of the radio buttons, the dialog correctly closes, but if the focus is on the spinner, I have to Tab away from it in order to use the Enter key.
How do I instruct the dialog that Enter really means "accept and close"?
Alternatively, how do I instruct (each) input field to relay an Enter to the "accept and close" handler?
Similarly, how do I instruct the dialog that Esc really means "cancel and close" even when the focus is on the spinner (or other field)?
how do I instruct (each) input field to relay an Enter to the "accept and close" handler?
The easiest approach is to define a "default button" on the dialog. Then when Enter is pressed the default button will be activated. Check out Enter Key and Button for different ways to do this.
how do I instruct the dialog that Esc really means "cancel and close"
Use Key Bindings to invoke the Action of your Cancel button.
First you define an Action to be used by the button:
public class CancelAction extends AbstractAction
{
public CancelAction()
{
super("Cancel");
putValue( Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_C) );
}
#Override
public void actionPerformed(ActionEvent e)
{
Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
if (window != null)
{
WindowEvent windowClosing = new WindowEvent(window, WindowEvent.WINDOW_CLOSING);
window.dispatchEvent(windowClosing);
}
}
}
Then you add the Action to the button so the user can use the mouse:
CancelAction cancelAction = new CancelAction();
cancelButton.setAction( cancelAction );
dialog.add(cancelButton);
Now you can use Key Bindings to bind the Escape key to the CancelAction so the user can use the keyboard:
KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE");
getRootPane().getActionMap().put("ESCAPE", cancelAction);
I suspect the reason I had problems is that a spinner is really a compound control, and the text (well, number) field is an component of that. So I needed to hook up the events to that subcomponent, rather than to the spinner itself:
// Make Ok/Cancel work when JSpinner has focus
getSpinnerField(jSpinnerOffset).addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
doOk();
}
});
where "getSpinnerField()" is just a shorthand in a private method:
private JFormattedTextField getSpinnerField(JSpinner spinner) {
return ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
}
Doing this, the Esc key automagically becomes able to dismiss the dialog.
Related
Simplified code
play = new PushButton("Play");
play.getUpFace().setImage(new Image(pathToImages+offImage));
play.getUpHoveringFace().setImage(new Image(pathToImages+hoverImage));
play.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
//This event is captured
}
});
I require a keyboard button as a shortcut to play button accordingly I have added
Event.addNativePreviewHandler(new NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(NativePreviewEvent event)
{ //Call the functionality of play button for the approriate key press
}
});
But once I click the play button using mouse the above nativePreviewHandler does not capture the key press event from keyboard.
So I added a keyDownHandler to the play button
play.addKeyDownHandler(new KeyDownHandler() {
#Override
public void onKeyDown(KeyDownEvent event) {
//This event is never fired
}
});
But the above onKeyDown() method is never called.
Only after I click at some other place on screen is the key press event captured by the nativePreviewHandler.
Just as additional info, I tried following without success
Added DomHandler to the play button instead of keyDownHandler
Removed the images set to the play button
Tried to unfocus the play button every time it is pressed using play.setFocus(false);
Any possible solutions or suggestions are appreciated.
The play button need to have the focus in order to be able to catch the keyDownEvent.
Could you check this ?
If you can't set the focus on that button: check this answer. You need to set a keyDownHandler to a container and ensure it is focused at all time in order to catch the events even if your button is not focused.
This will work:
Event.addNativePreviewHandler(new NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (event.getNativeEvent().getType().equals("keydown")) {
Window.alert("Code: " + Integer.toString(event.getNativeEvent().getKeyCode()));
}
}
});
I have a JComboBox component in the panel and ItemListener attached to it. But it gets fired after every up/down keypress (when scrolling though opened popup list). I want to change the selected value after the user accepts selection by pressing for example Enter key.
This is not a case when using mouse. When I move mouse over the combobox's list the highlight follows mouse pointer, but selected item is not changed until I press the mouse button. I would like to have the same behavior for keyboard, i.e. moving highlight via up/down arrow does not change selected item, but pressing Enter does.
I believe you should be able to do:
comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
after you have created your comboBox instance to get this functionality
In Java 8 they have fixed this behaviour, but only trigger if u set one UI property
UIManager.getLookAndFeelDefaults().put("ComboBox.noActionOnKeyNavigation", true);
the JComboBox.isTableCellEditor method works for arrow movement through the list, but does not work for type-ahead supported by the KeySelectionManager. i.e. you still get ActionEvents for every non-navigation key the user types, as the JComboBox interprets those characters for searching though the model to move to (or move close to) the user's intended selection.
this solution has a drawback in that it changes the action command for mouse clicks, which was a OK compromise for me because the the flow of the GUI forces the user to change the focus away from the combo box
I ended up making a special KeyListener, that relys on changing the combo box's default action command from comboBoxChanged to comboBoxMovement. Here's the line of code I need after my combo box is all initialized:
setExplicitSelectionManager(myComboBox);
... and here is the method and its contained class that do all the work:
private void setExplicitSelectionManager(JComboBox comboBox) {
class ExplicitSelectionManager implements KeyListener, FocusListener {
private JComboBox src;
private KeyListener superKeyListener;
ExplicitSelectionManager(JComboBox src) {
this.src = src;
// we like what the default key listener does, but not the action command
// it uses for ActionEvents it fires for plain text type-ahead characters
this.superKeyListener = src.getKeyListeners()[0]; // we only have one
src.removeKeyListener(superKeyListener); // will be replace right away, below
}
#Override
public void keyTyped(KeyEvent e) {
// basic combo box has no code in keyTyped
}
#Override
public void keyPressed(KeyEvent e) {
// in the default JComboBox implementation, the KeySelectionManager is
// called from keyPressed. I'm fine with the implementation of
// the default, but I don't want it firing ActionEvents that will cause
// model updates
src.setActionCommand("comboBoxMovement");
this.superKeyListener.keyPressed(e);
src.setActionCommand("comboBoxChanged");
if (e.getKeyCode() == 10) {
src.setSelectedIndex(src.getSelectedIndex());
}
}
#Override
public void keyReleased(KeyEvent e) {
// basic combo box has no code in keyReleased
}
#Override
public void focusGained(FocusEvent e) {
}
#Override
// this will also give us the event we want, if the user decides to Tab out of
// the combo box, instead of hitting Enter
public void focusLost(FocusEvent e) {
src.setSelectedIndex(src.getSelectedIndex());
}
}
ExplicitSelectionManager newSelectionManager = new ExplicitSelectionManager(comboBox);
comboBox.addKeyListener(newSelectionManager);
comboBox.addFocusListener(newSelectionManager);
}
... and here's the action performed method
private void comboBoxActionPerformed(java.awt.event.ActionEvent evt) {
JComboBox source = (JComboBox) evt.getSource();
// "comboBoxChanged" is the default,
// so any normal JComboBox can also use this action listener
if (evt.getActionCommand().equals("comboBoxChanged")) {
updateModel(source.getName(), (String) source.getSelectedItem());
}
}
Its the expected behavior with the ItemListener. whenever the displayed value changes the event is fired. For your requirement use an ActionListener.
i have a Jframe application with the defaultbutton set to btnClose_ (Close Button: this button closes the window).
I have 2 textfields that must also fire an event when user clicks the Enter key on the textfields. What happens is that when I press the Enter key while the cursor is on the textfield, the event on the Close button is fired causing the window to close.
Is it possible to remove the listener of the default button if the Enter key is pressed on the textfield? Here's my code for the textfield listener
/**
* Receives the two textfield instance
*/
private void addFilterListener(JTextField txf) {
txf.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been pressed.
*/
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
ActionListener al = btnClose_.getActionListeners()[0];
btnClose_.removeActionListener(al);
btnFilter_.doClick();
e.consume();
btnClose_.addActionListener(al);
}
}
});
}
private JButton getBtnClose(){
if(btnClose == null){
btnClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
getWindow().dispose();
}
});
}
return btnClose;
}
}
Where to start?
The first thing that springs out at me is the bad variable names. txf? What's wrong with proper words? textField or field, say. Or much better, a name descriptive of its purpose, not what it is.
Secondly, the first comment is wrong (not uncommon) and the second comment is redundant (already specified in the KeyListener interface, you don't need to try and half-heartedly specify it again).
Next up, low level key listeners tend not to work so well on Swing components (JComboBox being the most notorious example - it typically is implemented with child components). In general you can use JComponent.registerKeyboardAction (the API docs says this is obsolete but not deprecated, and to use more verbose code). For text components, you often want to play with the document (typically through DocumentFilter). In this particular case, looks like you just want to add an ActionListener.
Now doClick. It's a bit of an evil method. For one thing it blocks the EDT. It is probably the easiest way to make it look as if a button is pressed. From a programming logic point of view, it's best to keep away from modifying Swing components, when you can keep everything in your abstracted code.
Removing and adding listeners from components is generally a bad idea. Your code should determine what to do with an event including whether to ignore it. Do that at an appropriate point when handling the event. Don't duplicate state unnecessarily.
A potential issue is that the code seems to assume that there is precisely one action listener. There could be others. The code is not robust under unexpected behaviour. Set your components up at initialisation time, and you shouldn't need to refer to them again.
As far as I understood your question, you want that buttonClick should not get fired if Enter is pressed .
This won't fire doClick() if enter is pressed
if (e.getKeyCode() != KeyEvent.VK_ENTER) {
btnFilter_.doClick();
}
In the ActionListener of the close button, assuming you can change its code, don't close if one of the text fields have the focus.
public void actionPerformed(ActionEvent e) {
if (field1.hasFocus() || field2.hasFocus())
return; // don't close if text field has focus
frame.dispose();
}
If you can not change the ActionListener of the close button, add a FocusListener to the text fields. If one of them gets the focus, remove the default button. If the text field lost the focus, reset the default button.
FocusAdapter listener = new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
frame.getRootPane().setDefaultButton(null);
}
#Override
public void focusLost(FocusEvent e) {
frame.getRootPane().setDefaultButton(close);
}
};
field1.addFocusListener(listener);
field2.addFocusListener(listener);
This should be better than depending on the listeners being called in the correct sequence - it is of no avail to remove the listener if it was already called...
for my app I need the space key to call a function independent from the focused widget, everywhere in the app but only if the according tab is opend. I found that one can add a filter to the display, like this:
getShell().getDisplay().addFilter(SWT.KeyDown, new Listener() {
public void handleEvent(Event arg0) {
if( arg0.character == 32 ) { /**SPACE*/
if( mainTabs.getSelection().equals(analyseSoundFilesTab)) {
soundController.playButtonClickHandler();
}
}
}
});
That works fine most of the time, but if I give a button the focus via the "tab" or "shift tab", its kinda strange - the space bar will than activate a "button pressed", as if one clicks the button with the mouse. Im a bit stuck now, I don't know how to avoid this...
For the buttons, I have implemented a SelectionListener.
Regards.
You can use TraverseListener and disabled press event detection using doin field. Here is a sample code:
display.addFilter(SWT.KeyDown, new Listener() {
public void handleEvent(Event e) {
if (e.character == 32) {
System.out.printf("Space detected %s\n", e);
}
}
});
Button b1 = new Button(shell, SWT.PUSH);
b1.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent se) {
System.out.printf("Button pressed %s\n", se);
}
});
b1.addTraverseListener(new TraverseListener() {
#Override
public void keyTraversed(TraverseEvent te) {
System.out.printf("Traverse detected %s\n", te);
te.doit = true;
}
});
If addTraverseListener() didn't exist, your space button was detected after filter, so you would see "Space detected..." and after that "Button pressed...". Now that you set te.doit = true, you say to SWT to do space bar traversal (which does nothing actually) instead of firing key listener. You may optionally check te.detail to only prevent mnemonic traversals.
Choosing the 'Space key' is the real problem, because it is a general feature in most (all?) OS's that pressing space is equal to selecting the widget that has focus.
A way out would be using subclassed Button widgets that ignoring Space.
But it would confuse a lot of users, just because they expect that a focussed button is selected when they hit space and do not expect some other action.
I have the following bit of code in a method called by clicking the send button, or pressing enter in the message text field in a piece of code.
// In class ChatWindow
private void messageTextAreaKeyPressed(java.awt.event.KeyEvent evt) { // Event handler created by Netbeans GUI designer to call this method.
if(evt.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) {
sendMessage();
}
}
public void sendMessage() {
String currentMessage = messageTextArea.getText();
addMessage("You", currentMessage);
app.sendMessage(currentMessage, 1);
messageTextArea.setText("");
}
The last bit of code blanks the text area. However, after a message is sent by pressing the enter button, rather than being empty, the text box contains a newline.
My guess is that after my event handler runs, THEN the newline character is being added. How to I stop the newline being added?
try adding evt.consume() after your call to sendMessage()
private void messageTextAreaKeyPressed(java.awt.event.KeyEvent evt) {
if(evt.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) {
sendMessage();
evt.consume();
}
}
The default Action for the Enter key in a JTextArea is to insert a new line as you have seen. So the solution is to replace the default Action with a custom Action. The benefit of this approach is that this Action can also be used by the JButton (or JMenuItem etc.). An Action is basically the same as an ActionListener, all you need to do is implement the actionPerformed() method.
Read up on Key Bindings to see how this is done. All Swing components use Key Bindings.
as camickr said,
you should bind action to enter key;
Action sendAction = new AbstractAction("Send"){
public void actionPerformed(ActionEvent ae){
// do your stuff here
}
};
textarea.registerKeyboardAction(sendAction,
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED);
sendButton.setAction(sendAction);
if you are more interesed, I implemented Autoindent feature for textarea, using this technique:
here