So, key events are only dispatched on frames, and it seems that those happen
after timer events in the flash player. What was happening was that a KEY_UP event was coming in so long after the initial KEY_DOWN, that this code would think that the key was being held down even when not. So instead we wait until we actually see a 2nd KEY_DOWN event, at which point we may need to dispatch that event right away, or we may need to wait a bit if the limitRate is higher than the elapsed time between the two events. Now also there is a Timer for each key being held down. To top it all off, if you hold a key and then lose focus then this will continue to fire, so it's going to need yet more complication to fix that. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@358 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -29,6 +29,8 @@ import flash.events.TimerEvent;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.Timer;
|
||||
|
||||
import flash.utils.getTimer;
|
||||
|
||||
/**
|
||||
* A very simple class that adapts the KeyboardEvents generated by some source and altering
|
||||
* (or blocking) the key repeat rate.
|
||||
@@ -46,14 +48,10 @@ public class KeyRepeatLimiter extends EventDispatcher
|
||||
public function KeyRepeatLimiter (source :IEventDispatcher, limitRate :int = 0)
|
||||
{
|
||||
_source = source;
|
||||
_limitRate = limitRate;
|
||||
|
||||
_source.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
|
||||
_source.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
|
||||
|
||||
if (limitRate > 0) {
|
||||
_timer = new Timer(limitRate);
|
||||
_timer.addEventListener(TimerEvent.TIMER, handleTimerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,39 +62,50 @@ public class KeyRepeatLimiter extends EventDispatcher
|
||||
_source.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
|
||||
_source.removeEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
|
||||
|
||||
_down = new Dictionary();
|
||||
_keysDown = 0;
|
||||
if (_timer != null) {
|
||||
_timer.stop();
|
||||
// shut down any timers
|
||||
for each (var val :* in _down) {
|
||||
if (val is Array) {
|
||||
((val as Array)[0] as Timer).stop();
|
||||
}
|
||||
}
|
||||
_down = new Dictionary();
|
||||
}
|
||||
|
||||
protected function handleKeyDown (event :KeyboardEvent) :void
|
||||
{
|
||||
if (undefined === _down[event.keyCode]) {
|
||||
_keysDown++;
|
||||
_down[event.keyCode] = event.clone();
|
||||
var oldVal :* = _down[event.keyCode];
|
||||
if (oldVal === undefined) {
|
||||
_down[event.keyCode] = (_limitRate > 0) ? getTimer() : 0;
|
||||
|
||||
// maybe start the timer if we need to
|
||||
if (_timer != null && !_timer.running) {
|
||||
_timer.reset();
|
||||
_timer.start();
|
||||
} else if ((oldVal is Number) && _limitRate > 0) {
|
||||
var timeUntilRepeat :Number = _limitRate - (getTimer() - Number(oldVal));
|
||||
|
||||
var t :Timer = new Timer((timeUntilRepeat <= 0) ? _limitRate : timeUntilRepeat);
|
||||
t.addEventListener(TimerEvent.TIMER, handleTimerEvent);
|
||||
_down[event.keyCode] = [ t, event ];
|
||||
t.start();
|
||||
if (timeUntilRepeat > 0) {
|
||||
// don't dispatch this event, wait for our first repeat time
|
||||
return;
|
||||
}
|
||||
|
||||
// only dispatch if we're not limiting this key
|
||||
} else {
|
||||
// eat the event
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, dispatch the event
|
||||
dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleKeyUp (event :KeyboardEvent) :void
|
||||
{
|
||||
if (undefined !== _down[event.keyCode]) {
|
||||
delete _down[event.keyCode];
|
||||
_keysDown--;
|
||||
|
||||
if (_timer != null && _keysDown == 0) {
|
||||
_timer.stop();
|
||||
var oldVal :* = _down[event.keyCode];
|
||||
if (oldVal !== undefined) {
|
||||
if (oldVal is Array) {
|
||||
((oldVal as Array)[0] as Timer).stop();
|
||||
}
|
||||
delete _down[event.keyCode];
|
||||
}
|
||||
|
||||
// always dispatch an up
|
||||
@@ -105,9 +114,21 @@ public class KeyRepeatLimiter extends EventDispatcher
|
||||
|
||||
protected function handleTimerEvent (event :TimerEvent) :void
|
||||
{
|
||||
// dispatch any saved key events
|
||||
for each (var keyEvent :KeyboardEvent in _down) {
|
||||
dispatchEvent(keyEvent);
|
||||
// dispatch the key event associated with this timer
|
||||
for each (var val :* in _down) {
|
||||
if (val is Array) {
|
||||
var arr :Array = val as Array;
|
||||
var timer :Timer = arr[0] as Timer;
|
||||
if (timer == event.target) {
|
||||
if (timer.delay != _limitRate) {
|
||||
timer.reset();
|
||||
timer.delay = _limitRate;
|
||||
timer.start();
|
||||
}
|
||||
dispatchEvent(arr[1] as KeyboardEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +138,6 @@ public class KeyRepeatLimiter extends EventDispatcher
|
||||
/** Tracks whether a key is currently being held down. */
|
||||
protected var _down :Dictionary = new Dictionary();
|
||||
|
||||
/** How many keys are currently down? */
|
||||
protected var _keysDown :int;
|
||||
|
||||
/** The timer generating key repeats, if any. */
|
||||
protected var _timer :Timer;
|
||||
protected var _limitRate :int;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user