diff --git a/src/as/com/threerings/flash/KeyRepeatBlocker.as b/src/as/com/threerings/flash/KeyRepeatLimiter.as similarity index 52% rename from src/as/com/threerings/flash/KeyRepeatBlocker.as rename to src/as/com/threerings/flash/KeyRepeatLimiter.as index 98b1d747..a6641f21 100644 --- a/src/as/com/threerings/flash/KeyRepeatBlocker.as +++ b/src/as/com/threerings/flash/KeyRepeatLimiter.as @@ -23,27 +23,37 @@ package com.threerings.flash { import flash.events.EventDispatcher; import flash.events.IEventDispatcher; - import flash.events.KeyboardEvent; +import flash.events.TimerEvent; import flash.utils.Dictionary; +import flash.utils.Timer; /** - * A very simple class that adapts the KeyboardEvents generated by some source by discarding - * the extra KEY_DOWN events generated while a key is held down. + * A very simple class that adapts the KeyboardEvents generated by some source and altering + * (or blocking) the key repeat rate. */ -public class KeyRepeatBlocker extends EventDispatcher +public class KeyRepeatLimiter extends EventDispatcher { /** - * Create a KeyRepeatBlocker that will be blocking key repeat events from + * Create a KeyRepeatLimiter that will be limiting key repeat events from * the specified source. + * + * @param limitRate 0 to block all key repeats, or a millisecond value specifying how often + * to dispatch KEY_DOWN events while the key is being held down. The rate will be limted + * by the frame rate of the enclosing SWF. */ - public function KeyRepeatBlocker (source :IEventDispatcher) + public function KeyRepeatLimiter (source :IEventDispatcher, limitRate :int = 0) { _source = source; _source.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown); _source.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp); + + if (limitRate > 0) { + _timer = new Timer(limitRate); + _timer.addEventListener(TimerEvent.TIMER, handleTimerEvent); + } } /** @@ -53,26 +63,64 @@ public class KeyRepeatBlocker extends EventDispatcher { _source.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown); _source.removeEventListener(KeyboardEvent.KEY_UP, handleKeyUp); + + _down = new Dictionary(); + _keysDown = 0; + if (_timer != null) { + _timer.stop(); + } } protected function handleKeyDown (event :KeyboardEvent) :void { - if (!_down[event.keyCode]) { - _down[event.keyCode] = true; + if (undefined === _down[event.keyCode]) { + _keysDown++; + _down[event.keyCode] = event.clone(); + + // maybe start the timer if we need to + if (_timer != null && !_timer.running) { + _timer.reset(); + _timer.start(); + } + + // only dispatch if we're not limiting this key dispatchEvent(event); } } protected function handleKeyUp (event :KeyboardEvent) :void { - delete _down[event.keyCode]; + if (undefined !== _down[event.keyCode]) { + delete _down[event.keyCode]; + _keysDown--; + + if (_timer != null && _keysDown == 0) { + _timer.stop(); + } + } + + // always dispatch an up dispatchEvent(event); } + protected function handleTimerEvent (event :TimerEvent) :void + { + // dispatch any saved key events + for each (var keyEvent :KeyboardEvent in _down) { + dispatchEvent(keyEvent); + } + } + /** Our source. */ protected var _source :IEventDispatcher; /** 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; } }