Skip to content

Instantly share code, notes, and snippets.

@seronis
Created June 8, 2013 17:10
Show Gist options
  • Save seronis/5735902 to your computer and use it in GitHub Desktop.
Save seronis/5735902 to your computer and use it in GitHub Desktop.
AWTEventListener to enforce exactly one keyPressed/keyReleased event is triggered per user action with no superfluous repeats.
package seronis.util;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.Timer;
public class DisableKeyRepeats implements AWTEventListener {
private int grace = 0;
private Set<Integer> _pressed = new HashSet<Integer>();
Map<Integer,KeyUpHandler> _upevents = new HashMap<Integer,KeyUpHandler>();
public DisableKeyRepeats() {
this.grace = 10;
}
public DisableKeyRepeats( int _val ) {
this.grace = _val;
}
public interface ConfirmedRelease { /* wrapper tag */
}
public void install() {
Toolkit.getDefaultToolkit().addAWTEventListener(this,
AWTEvent.KEY_EVENT_MASK);
}
public void uninstall() {
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
}
private static boolean assertEDT() {
if (EventQueue.isDispatchThread())
return true;
throw new AssertionError("ERROR: AWTEventListener installed to ["
+ Thread.currentThread() + "].");
}
@Override
public void eventDispatched(AWTEvent event) {
assert event instanceof KeyEvent;
assert assertEDT();
final KeyEvent keyEvent = (KeyEvent) event;
if (keyEvent.isConsumed()) {
return;
}
if (keyEvent instanceof ConfirmedRelease) {
return;
}
if (keyEvent.getID() == KeyEvent.KEY_TYPED) {
return;
}
if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
if (_pressed.contains(keyEvent.getKeyCode())) {
keyEvent.consume();
} else {
_pressed.add(keyEvent.getKeyCode());
}
KeyUpHandler action = (KeyUpHandler) _upevents.get(keyEvent.getKeyCode());
if (action != null) {
action.cancel();
}
return;
}
if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
keyEvent.consume(); //NOTE: only confirmed release events are passed on
Timer timer = new Timer(this.grace, null);
KeyUpHandler handler = new KeyUpHandler(keyEvent,timer);
timer.addActionListener(handler);
// timer.setRepeats(false); //NOTE: unneeded since we call timer.stop() in handler
timer.start();
KeyUpHandler expired = _upevents.put(keyEvent.getKeyCode(),handler);
if( expired != null ){
expired.cancel();
}
return;
}
//NOTE: here should be unreachable
throw new AssertionError("AWT Key Listener: strange ID encountered (" + keyEvent.getID() + ")");
}
protected class KeyUpHandler implements ActionListener {
private final KeyEvent _keyevent;
private Timer _timer;
KeyUpHandler(KeyEvent event, Timer timer) {
_keyevent = event;
_timer = timer;
}
void cancel() {
assert assertEDT();
_timer.stop();
_timer = null;
_upevents.remove(Integer.valueOf(_keyevent.getKeyCode()));
}
@Override
public void actionPerformed(ActionEvent arg0) {
if( _timer == null )
return;
else cancel();
_pressed.remove(Integer.valueOf(_keyevent.getKeyCode()));
KeyEvent repost = new ConfirmedReleaseEvent(
(Component) _keyevent.getSource(),
_keyevent.getID(), _keyevent.getWhen(),
_keyevent.getModifiers(),
_keyevent.getKeyCode(),
_keyevent.getKeyChar(),
_keyevent.getKeyLocation());
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(repost);
}
}
@SuppressWarnings("serial")
public static class ConfirmedReleaseEvent extends KeyEvent implements ConfirmedRelease {
public ConfirmedReleaseEvent(Component source, int id, long when, int modifiers,
int keyCode, char keyChar, int keyLocation) {
super(source, id, when, modifiers, keyCode, keyChar, keyLocation);
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment