capturing global keypresses in Java - java

So I want to trigger an event (pausing/unpausing some media) whenever the user presses spacebar anywhere in the my Swing app.
Since there are so many controls and panels that could have focus, its not really possible to add keyevents to them all(not to mention gross).
So I found
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher()
which is awesome, you can register global keypress pre-handlers. There's a major problem though - spaces will be typed all the time in input fields, table cells, etc, and I obviously dont want to trigger the pause event then!
So any ideas?
Perhaps there is way to detect globally whether the cursor is focused on something that allows text input, without having to check through a list of all the editable controls(vomit!)?

I think you answered that yourself - yes I think you can find out the current element that has focus, and if it is an instanceof a certain field class, you ignore the space for the purpose of pause event. If it seams heavy handed, don't worry, instanceof is VERY fast for the JVM (and in any cause you are talking human scale events which are an eon to a processor).

I'm rusty on my Swing, but I think you should try registering a global listener using Toolkit.addAWTEventListener with a KEY_EVENT_MASK. You can then filter the AWTEvent processing based on its type and source.

Ok...
Well im trying to filter based on source. Problem is my editable ComboBoxes...
They are instanceof
javax.swing.plaf.basic.BasicComboBoxEditor$BorderlessTextField
And since BorderlessTextField is a private inner class, I apparently cant do an instanceof check against it.
ideas?
EDIT:
ok so this works....
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(new KeyEventPostProcessor() {
public boolean postProcessKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
Object s = e.getComponent();
if (!(s instanceof JTextField) &&
!(s instanceof JTable && ((JTable) s).isEditing())
) {
music_player.pauseEvent();
}
//System.out.println(s.getClass().toString());
}
return true;
}
});
Its totally gross and I hate it.
I wish I could figure out a way to check if the keypress has been consumed by any component - then I could only perform the pause event when the keypress was not actioned by any component.

Related

"Global" hotkey EXCEPT in text entry

I'm working on a large application with a lot of different windows/frames/panes/tabs, many of which may be open at any time. Requirements call for some hotkeys that give focus to a certain tab in a certain window. I would like to avoid registering a KeyListener with every single window/component that might have focus; as such, I tried the following
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher(){
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getKeyChar() == '-'){
doTheThing();
return false;
}
return false;
}
});
That first "return false" allows that character to go through to other possible consumers, such as text entry fields. The problem is that the code still fires, and will jerk the user back to the tab in question (potentially pulling them away from what they were doing, if it was another tab in the same window.)
Is there a way to have this code fired globally, EXCEPT if it would otherwise enter into a text panel? (Maybe a way to add a handler that would get it "last" rather than "first" - so it only fires if nothing else is consuming the event?)
I'm pretty new to UI design, so apologies if this is a dumb question, and thanks in advance for the help.
Try using KeyboardFocusManager#getFocusOwner and determine if it's a JTextComponent or not...
if (!(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() instanceof JTextComponent)) {
//...
}
or some such

Is it possible to enable all the text fields at once in Java Swing?

I have multiple text fields which I need to enabled or disabled at once using Java Swing.
Is that possible?
If all the JTextFields are on a single container, you could do :
for (Component c : container.getComponents()) {
if (c instanceof JTextField) {
c.setEnabled(false);
}
}
if you put them all in a linked list / array list you could have a method to loop though it and enable / disable. This is probably the easiest way
It will be hard to maintain global list of all components and iterate over all references. Let's say you need to notify all text fields to become enabled or disabled without breaking loosely-coupled nature of your system (I assume you are interested to keep your application maintainable).
My suggestion is:
Sub-class JTextField
Start using EventBus to decrease coupling (you can start with any implementation, this one is simple enough)
In your JTextField sub-class subscribe to receive change_state event
Use your own JTextField whenewer you want to have enable/disable supported
Generate change_state event from any part of your application
Please note, there will be(should be) only two places related to the requested functionality:
event triggering (can be button action listener)
event processing (JTextField sub-class)
Do not spread event processing logic over your application. Happy coding.

Prevent Java Swing JTable losing focus when invalid data entered

We currently have a focus problem with a JTable/JTextEditor in java swing. The JTable has a custom cell editor which is a JTextField.
The issue is when a cell is being edited and contains invalid data, and the user clicks on a JButton, the text field will stop editing and the JButton actionPerformed (clicked) is called. The JTable#setValueAt handles validation so if the data in the JTextField is invalid, the underlying TableModel is not updated.
Ideally, we do not want to let the JButton click occur. Focus should remain with the JTable or the JTextField.
Clicking the button will perform a submit action and close the frame the table is in. As the validation in the TableModel#setValueAt does not update the value, it submits the old value.
Can this be done? I am still fairly new to Swing so I am not aware what to check.
Unfortunately, our code is not straight forward. The UI is constructed from XML in such a way that the button knows nothing about anything else on a form (this is code I have inherited).
In .net you could stop a control losing focus by handling a Validating event and setting a cancel flag. Is there a similar mechanism with Java.
Validating the input after editing has concluded, in setValueAt(), may be inconveniently late. The editor itself can preclude navigation for invalid values, as shown in this example that links to the corresponding tutorial section.
For valid values, you can make the table commit when losing focus:
table.putClientProperty("terminateEditOnFocusLost", true);
Can you try using inputverifier on the editor component, i.e. text field?
When the focus is lost from a component, the lost focus method is called (more reference in http://docs.oracle.com/javase/tutorial/uiswing/events/focuslistener.html). Therefore, you may call the validation method when you lose the focus.
If you do not need to be aware of the specific field being edited, you can also perform validation inside your button and prevent the submission if it is not sucessful.
I'd achieved a similar functionality by overriding the stopCellEditing method in my JTable's CellEditor.
#Override
public boolean stopCellEditing() {
String s = (String) getCellEditorValue();
if (s != null) {
if (!testYourValue()) {
Toolkit.getDefaultToolkit().beep();
return false;
}
}
return super.stopCellEditing();
}

Java KeyListeners/KeyBinding - Clear Buffer - Java Game Development

I'm working on a Java Game and I've come to a point where I'm having problems with the KeyListeners/KeyBinding. What I basically want to do is temporarily disable the keyboard/do not allow more inputs when an animation occurs. This animation is generated by updating data.
What I currently get is that I press the key for the animation, the animation starts, and I press another key that does some other function. It gets added to the stack/queue(?) of the keyboardlistener and triggers when the first animation finishes.
I'm using a JPanel that implements KeyListener.
To give an idea of the code:
public void keyPressed(KeyEvent arg0) {
//Prevents Repeated keys
pressed.add(arg0);
if (pressed.size() == 1) {
int key = ((KeyEvent) pressed.toArray()[0]).getKeyCode();
if (key == KeyEvent.VK_ENTER) {
doSomeAnimation();
} else if (key == KeyEvent.VK_SPACE) {
doADifferentAnimation();
}
Update();
}
}
Things I've tried:
1) Set the focusable(false) on the JPanel before the calls to the animations. Then set the focusable(true) and grab the focus when they've been completed.
2) Used a boolean to track when an animation occurred.
3) Use Key Bindings.
No matter what method I used, I always ended up with the problem that I would still take in input from the keyboard when the animation was occurring. Then, once that animation was finished, it'd go to the next element in the stack/queue(?) and process that. Also, these animations would need to occur more than once (so using an array of booleans to verify if it's been executed already wouldn't be helpful).
So, if you have any idea or help (or places to point me to) that would be greatly appreciated.
Some Extra Information: Java 1.6, IDE Eclipse, MVC structure. (This in question is the Controller/Model)
Assuming your animation is driven by an instance of javax.swing.Timer, the queue in question is the EventQueue, which runs events in "the same order as they are enqueued" by the Timer. Because it is impractical to stop other devices on the host platform from evoking your listeners, you have to track the effect in your application. As a concrete example, this game has several overloads of the model's move() method to handle input from disparate sources: keyboard, mouse or animation timer. The timer may be toggled on or off to see its effect. This example, which supplants the EventQueue with a custom implementation, may also offer some insight.

action listeners and event sources in Swing

OK, so if I add an ActionListener to a GUI element, and it's the only element I use that ActionListener with, does it matter which of the following lines (a,b) I use to get the checkbox selected state?
final JCheckBox checkbox = (JCheckBox)this.buildResult.get("cbDebugTick");
checkbox.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent event){
boolean bChecked =
// (a) checkbox.isSelected();
// (b) ((JCheckBox)event.getSource()).isSelected();
model.setPrintDebugOn(bChecked);
}
});
It makes sense to me that if I add the ActionListener object to multiple GUI elements, then I should use (b).
And in (b), is it OK to blindly cast event.getSource() to JCheckBox, since I'm the one who added the action listener, or should I program defensively and do an instanceof check?
note: this question is in the context of event listeners in general; kdgregory has some good points below specifically re: checkboxes which I had neglected to consider.
I'd do neither.
If clicking the checkbox is going to start some action, I'd attach an ItemListener, then just look at the selection state in the ItemEvent.
However, checkboxes don't normally invoke actions, they manage state. So a better approach is to examine all of your checkboxes in response to whatever does kick off the action.
Edit: some commentary about the larger issues that the OP raised.
First, it's important to realize that large parts of Swing represent implementation convenience rather than a coherent behavior model. JCheckBox and JButton have nothing in common other than the fact that clicking within their space is meaningful. However, they both inherit from AbstractButton, which provides implementation details such as the button's label. It also assumes that buttons are "pressed", and that pressing a button will initiate some meaningful behavior (the action). In the case of JCheckbox, however, the button press is not important, the change in state is. That state change is signaled to the ItemListener -- which is also defined on AbstractButton even though state changes are meaningless to other button types (the JavaDoc even says "checkbox").
One of the things that Swing did get right -- if hard to use -- is the idea of that an Action is separate from the control initiating that action. An Action object can be invoked from multiple controls: a menu item, a pushbutton on a dialog, a keystroke, whatever. More important from a design perspective is that it takes you away from the idea of a generic "listener" that tries to figure out what needs to happen. I've seen apps where a single listener receives input from the entire menu system, for example, and then runs through a big if/else chain to figure out which menu item was pressed. Using Actions means you have more classes, but in the long run gives you a more maintainable app.
Finally, from a usability perspective, there's a difference between controls that maintain state, such as JCheckbox and JTextArea, and those that initiate actions, such as JButton and JMenuItem. I have seen a (web) app where clicking on a radio button takes you to a different page. That's bad. Even if you're planning to use listeners internally, to update the state of some model, you should ask yourself why the collection of GUI elements do not in themselves provide you with a model.
For the case where the listener is exclusive (such as an anon listener), I use (a).
If the listener will be reused (eg, this is an instance of ActionListener) I'll write it as:
#Override
public void actionPerformed(ActionEvent event) {
Object src = event.getSource();
if (src == checkbox) {
boolean bChecked = checkbox.isSelected();
// ...
}
}
If you have several checkboxes and they are processed the same way, then instanceof makes sense.
in (b) to be rigourous, you should indeed do a instanceof check, but it's not that important. I would think both these lines are fine and acceptable, though (b) would be "better code"
Although, what is usually done in an action listener is simply call another method customized to your checkbox. So it would look like something like this:
#Override public void actionPerformed(ActionEvent event) {
//your treatment would be in this method, where it would be acceptable to use (a)
onCheckBoxActionPerformed(event)
}
I'd program with b defensively as it's the best-practice option. But if only you are ever going to use the code then there is no reason why you can't do a. However, imagine how happy you will be with yourself if you come back to it at some future point, change something and find you wrote good code which you can directly reuse...

Categories