Created
June 8, 2013 17:10
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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