Finally, a ComboBox that works.
The AS3 ComboBox has always been really irritating in data-driven apps. In short, it just doesn't work the way you expect a dropdown menu to work as far as keyboard accessibility. Any time you hit a key, it jumps to the first label starting with that letter, instead of letting you keep typing a word out. This irritates clients, who might have a combobox with a few thousand usernames in it. If three hundred of them start with "S", they have to type "S" and scroll down or arrow down to find the right one. Worst of all, it's very easy to forget that an app works this way, so there's the repeated aggravation of starting to type and then remembering that AS3 components are stupid. Or, as a client asked me, "why the **** can't it just work like everything else on the web?"
Well, now it can. The following is a drop-in replacement for ComboBox that works the way everything else on the web does. It's fast, fun and free. I'm putting this out for all to enjoy.
Cheers,
Josh
Code:
package {
import flash.utils.Timer;
import flash.ui.Keyboard;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import fl.controls.ComboBox;
import fl.data.DataProvider;
public class StrikeCB extends ComboBox {
private var buffer:String = "";
private var timer:Timer = new Timer(400);
private var dict:Array = new Array();
private var ds:Array;
public function StrikeCB() {
super();
init();
}
public function init():void {
addEventListener(Event.ADDED_TO_STAGE,addListeners,false,0,true);
addEventListener(Event.REMOVED_FROM_STAGE,remListeners,false,0,true);
}
private function addListeners(evt:Event):void {
this.addEventListener(FocusEvent.FOCUS_IN,openListen,false,0,true);
this.addEventListener(FocusEvent.FOCUS_OUT,openListen,false,0,true);
timer.addEventListener(TimerEvent.TIMER,clearBuffer,false,0,true);
buffer = "";
}
private function remListeners(evt:Event):void {
removeEventListener(FocusEvent.FOCUS_IN,openListen);
removeEventListener(FocusEvent.FOCUS_OUT,openListen);
timer.stop();
timer.removeEventListener(TimerEvent.TIMER,clearBuffer);
buffer = "";
}
private function openListen(evt:Event):void {
if (evt.type == "open" || evt.type=="focusIn") {
addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown,false,0,true);
} else {
removeEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
}
}
private function onKeyDown(evt:KeyboardEvent):void {
if (evt.charCode == Keyboard.SPACE) return (void);
buffer += String.fromCharCode(evt.charCode);
if (!ds) ds = dict.slice();
for (var m:int = 0;m<buffer.length;m++) {
for (var k:String in ds) {
if (ds[k] === null) continue;
if ((ds[k].length<buffer.length ||
buffer.substr(m,1).toLowerCase()!=ds[k].substr(m,1).toLowerCase()) &&
ds.length>1) {
ds[k] = null;
}
}
}
for (var u:String in ds) {
if (ds[u]!=null) {
this.selectedIndex = int(u);
this.highlightCell(int(u));
this.dropdown.scrollToIndex(int(u));
break;
}
}
timer.reset();
timer.start();
}
private function clearBuffer(evt:TimerEvent):void {
buffer = "";
ds = null;
}
override public function set dataProvider(arg0:DataProvider):void {
super.dataProvider = arg0;
for (var i:int=0;i<arg0.length;i++) {
dict[i] = arg0.getItemAt(i).label;
}
}
override protected function highlightCell(arg0:int=-1):void {
if (arg0>-1) selectedIndex = arg0;
super.highlightCell(arg0);
}
}
}