Menu

Spark ComboBox

SourceForge Editorial Staff

Jason Szeto (Dev)
Jacob Goldstein (QA)

Functional and Design Specification


Glossary


  • ComboBox - a component that consists of a TextInput, a button and a List popup
  • DropDownList a non-editable ComboBox that does not contain a TextInput
  • filtering the behavior of narrowing down the set of items based on what the user has typed
  • input string - the string typed into the TextInput
  • matching item - the item in the dataProvider that is a string match to the input string

Summary and Background


The ComboBox component is similar to a DropDownList but has a TextInput instead of a Label. A user can type into the TextInput and the dropDown will scroll to and highlight the closest match.

Users are allowed to type in an item not found in the dataProvider. With this behavior, a ComboBox acts as a list of suggested values, while a DropDownList acts as a list of possible values.

Much of the functionality and API of ComboBox is inherited from DropDownList.
DropDownList Spec

Usage Scenarios


Mick is creating an online survey for the PitchSpoon music site. One of the questions is to select the "Best Rock Band of All Time." Since the list of choices is long and he doesn't want to take too much screen real estate, he uses a ComboBox to capture the answer. He also wants to allow write-in answers which is why he uses a ComboBox instead of a DropDownList. He adds the ComboBox in his application, hooks up the dataProvider to his back end data.

Jonathan is creating a drawing application. He needs a way for the user to choose a stroke weight. The stroke weight can range in size from 0 to 100. He decides to use a ComboBox to provide ten commonly used weights but still allow the user to type in a weight. Each stroke weight shown in the dropDown is accompanied by a black line that shows a visual representation of the stroke weight.

Margo is creating a web browser application. She uses a ComboBox for the URL location bar. She uses the client's SharedObject to retrieve recently typed URLs and passes that into the ComboBox's dataProvider. Each dropDown item shows the fully qualified URL and a page icon (if one exists). When the user types in a URL and commits it (closes the ComboBox), then that URL is written out to the SharedObject.

Detailed Description


The ComboBox component is a DropDownListBase with a textInput skin part of type TextInput. DropDownList will be refactored into DropDownList and DropDownListBase. DropDownList will add support for a label skin part.

For ComboBox, the user can type into the TextInput control. The property openOnInput controls whether the dropDown will open upon typing. If true, the dropDown will open and scroll to and highlight the closest matching item in the dataProvider. By default, it is true.

The user is allowed to type in an item that isn't in the dataProvider. In this case, the selectedIndex will be CUSTOM_SELECTED_ITEM and the selectedItem will be an object constructed from the input string. ComboBox will provide a property, labelToItemFunction, which is a Function that is called to convert the input string into an item.

The default matching algorithm uses some basic string comparison. If an input string of length n is equivalent to the first n characters of an item (ignoring case), then it is a match to that item. For example, 'aRiz' is a match to "Arizona" while 'riz' is not.

The property itemMatchingFunction is called to return a set of indicies in the dataProvider that match the input string. Developers can pass in their own function to customize the matching algorithm.

The ComboBox skin is very similar to DropDownListSkin except that it replaces the Label skin part with a TextInput skin part.

TextInput Proxy

The ComboBox will proxy some events and properties from TextInput.

Properties:

  • maxChars
  • restrict

Styles:

  • paddingLeft
  • paddingRight
  • paddingBottom
  • paddingTop

Some properties don't make sense to expose since their value should never change (ex. displayAsPassword, editable, selectable). The properties that are not exposed are provide advanced functionality and can be accessed through the textInput skin part.

Inheriting styles are not directly exposed because they get passed down to the textInput. Non-inheriting styles are not directly exposed because you can use Advanced CSS to specify the styles. The exception are the padding styles which are defined on ComboBox as a convenience for developers.

None of the events are exposed since they would only be used by advanced developers. These events can be accessed by listening to the events on the textInput skin part.

Mouse and Keyboard interactions

Keyboard interactions while closed:

  • Editing the textInput (ie. typing a letter or deleting characters) will open the dropDown. The dropDown will scroll to and highlight the closest match. If there is no match, then no item will be highlighted. If there is a match, the textInput will contain the match, with the non-typed characters selected.
  • Pressing LEFT/RIGHT/SHIFT-LEFT/SHIFT-RIGHT/SHIFT-UP/SHIFT-DOWN in the textInput behaves the same as in TextInput
  • Pressing UP/DOWN/HOME/END/PGUP/PGDN behaves the same way as in DropDownList. The selectedItem will change depending on upon which navigation key was pressed.
  • Pressing CTRL-DOWN will open the dropDown

Keyboard interactions while open:

  • Editing the textInput will scroll to and highlight the closest match. If there is no match, then no item will be highlighted.
  • Pressing LEFT/RIGHT/SHIFT-LEFT/SHIFT-RIGHT/SHIFT-UP/SHIFT-DOWN in the textInput behaves the same as in TextInput
  • Pressing UP/DOWN/HOME/END/PGUP/PGDN will change the selectedItem and update the textInput to that item. The selectedItem will change depending on upon which navigation key was pressed. The textInput text will be selected.
  • Pressing ENTER will close the dropDown. It will commit the highlighted item if one exists. If no highlighted item exists, then it will commit the input string.
  • Pressing CTRL-UP will close the dropDown and commit the input string.
  • Pressing ESC will close the dropDown and cancel the commit. The selectedItem will not change, but the textInput text will remain the same.

Mouse interactions while closed:

  • Clicking in the textInput will put focus in the textInput. If it was not previously in focus, then all of text in the textInput will be selected. Otherwise, it will behave the same as TextInput.
  • Clicking on the openButton will open the dropDown, highlight the closest match to the input string and select the non-typed characters.

Mouse interactions while open:

  • Clicking in the textInput will put focus in the textInput. If it was not previously in focus, then all of text in the textInput will be selected. Otherwise, it will behave the same as TextInput.
  • Clicking on the openButton will close the dropDown and commit the input string. It will remove the focus ring from the ComboBox and remove focus from the textInput.

B-Features

Filtering
When the user types into the TextInput, only items that match the input string will appear in the dropDown. The dropDown will still scroll to and highlight the closest matching item.

The ComboBox will only support filtering if the dataProvider implements ICollectionView. ComboBox filtering uses the built-in filtering support in the implementors of ICollectionView, such as ArrayCollection.

The default filtering algorithm uses the same algorithm as the matching algorithm described previously.

The property filterFunction points to a function that is passed to ICollectionView.filterFunction. This function returns true if the item matches the input string.

Allow user-defined selectedItem
By default, the ComboBox will allow the user to type in a value that is not in the dataProvider. The allowCustomSelectedItem property controls whether or not the user can commit an item not found in the dataProvider.

Add State support
Add in support for putting the ComboBox into different states. Possible states include itemSelected, noItemSelected, and itemMatched. By having these states, the skin could change its appearance based on which state it is in.

Prompt Support
Support the prompt property. If prompt is set, then the prompt will be displayed in the TextInput if selectedIndex == NO_SELECTION. The prompt will never be committed as the selectedItem. The prompt will be displayed in another color/alpha text style. New style properties will need to be added for this support.

API Description


Additions to MXML Language and ActionScript Object Model

Include the relevant API for this feature using the following example as a guideline. Make sure you indicate whether APIs are public or protected. You do not need to present private or mx_internal APIs here.

package spark.components
{    
    import mx.controls.ComboBox;

    /**
     *  Botttom inset in pixels for the textInput
     * 
     *  @default 3
     */
    <a href="Style%28name%3D%26quot%3BpaddingBottom%26quot%3B%2C%20type%3D%26quot%3BNumber%26quot%3B%2C%20format%3D%26quot%3BLength%26quot%3B%2C%20inherit%3D%26quot%3Bno%26quot%3B%29">Style(name="paddingBottom", type="Number", format="Length", inherit="no")</a>

    /**
     *  Left inset in pixels for the textInput
     * 
     *  @default 3
     */
    <a href="Style%28name%3D%26quot%3BpaddingLeft%26quot%3B%2C%20type%3D%26quot%3BNumber%26quot%3B%2C%20format%3D%26quot%3BLength%26quot%3B%2C%20inherit%3D%26quot%3Bno%26quot%3B%29">Style(name="paddingLeft", type="Number", format="Length", inherit="no")</a>

    /**
     *  Right inset in pixels for the textInput
     * 
     *  @default 3
     */
    <a href="Style%28name%3D%26quot%3BpaddingRight%26quot%3B%2C%20type%3D%26quot%3BNumber%26quot%3B%2C%20format%3D%26quot%3BLength%26quot%3B%2C%20inherit%3D%26quot%3Bno%26quot%3B%29">Style(name="paddingRight", type="Number", format="Length", inherit="no")</a>

    /**
     *  Top inset in pixels for the textInput
     * 
     *  @default 5
     */
    <a href="Style%28name%3D%26quot%3BpaddingTop%26quot%3B%2C%20type%3D%26quot%3BNumber%26quot%3B%2C%20format%3D%26quot%3BLength%26quot%3B%2C%20inherit%3D%26quot%3Bno%26quot%3B%29">Style(name="paddingTop", type="Number", format="Length", inherit="no")</a>


    public class ComboBox extends DropDownListBase
    { 
        /**
         *  Optional skin part that holds the input text or the selectedItem text 
         */
        <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>
        public var textInput:TextInput;

        /**
         *  Static constant representing the value "custom selectedItem"
         */
        public static const CUSTOM_SELECTED_ITEM:int = -3;


        public function ComboBox()
        {
            super();
        }

        /**
         *  The function referenced by this propery is called when textInput's text
         *  is committed and is not found in the dataProvider. 
         * 
         *  The function signature is this:
         * 
         *  function myLabelToItem(value:String):Object
         * 
         *  The function returns an Object that should be the same type as the items 
         *  in the dataProvider.
         * 
         *  If the value is null, then this function will not be called and the selectedItem
         *  will be set to the input string. 
         * 
         *  @default null 
         */ 
        public function set labelToItemFunction(value:Function):void;
        public function get labelToItemFunction():Function;

        /**
         *  If true, the dropDown will open whenever the user edits the textInput.
         * 
         *  @default true 
         */ 
        public function set openOnInput(value:Boolean):void;
        public function get openOnInput():Boolean;


        /**
         *  The function referenced by this property takes an input string and returns
         *  the items in the dataProvider that match the input. 
         *  The items are returned as a Vector of indicies in the dataProvider. 
         * 
         *  The function signature is this:
         * 
         *  function myMatchingFunction(comboBox:ComboBox, inputText:String):Vector.<int>
         * 
         *  If the value is null, the ComboBox will find matches using the following algorithm:
         * 
         *  If an input string of length n is equivalent to the first n characters 
         *  of an item (ignoring case), then it is a match to that item. For example, 'aRiz' 
         *  is a match to "Arizona" while 'riz' is not.
         * 
         *  @default null
         */ 
        public function set itemMatchingFunction(value:Function):void;
        public function get itemMatchingFunction():Function;


        ////////////////////////////////////////////////////////////////
        //  TextInput Proxy Properties and Methods
        ////////////////////////////////////////////////////////////////

        /**
         *  The maximum number of characters that the textInput can contain, as entered by a user.   
         * 
         *  @default 0
         */ 
        public function set maxChars(value:int):void;
        public function get maxChars():int;

        /**
         *  Indicates the set of characters that a user can enter into the textInput.  
         * 
         *  @default ""
         */ 
        public function set restrict(value:String):void;
        public function get restrict():String;

        ////////////////////////////////////////////////////////////////
        //  B-Features
        ////////////////////////////////////////////////////////////////

        /**
         *  If true, then the user can commit the text in the textInput even if it is
         *  not found in the dataProvider.
         * 
         *  @default true
         */  
        public function set allowCustomSelectedItem(value:Boolean):void;
        public function get allowCustomSelectedItem():Boolean;

        /**
         *  The filtering function passed to ICollectionView.filterFunction.
         */ 
        public function set filterFunction(value:Function):void;
        public function get filterFunction():Function;


    }
}

B Features


  • Filtering
  • Allowing user-defined selectedItem
  • States
  • Prompt

All are described in the Detailed Description section

Examples and Usage


<ComboBox width="140"> 
    <dataProvider>
         <ArrayCollection>
             <String>1. Alabama</String>
             <String>2. Alaska</String>
             <String>3. Arizona</String>
             <String>4. Arkansas</String>
             <String>5. California</String>
         </ArrayCollection>
     </dataProvider>
</ComboBox>

\

<s:Skin xmlns:local="*" xmlns:fx="http://ns.adobe.com/mxml/2009" 
xmlns:s="library://ns.adobe.com/flex/spark" 
xmlns:mx="library://ns.adobe.com/flex/halo" alpha.disabled=".5">


    <!-- host component -->
    <fx:Metadata>
        <![CDATA[ 
        /** 
        * @copy spark.skins.spark.ApplicationSkin#hostComponent
        */
        <a href="HostComponent%28%26quot%3BComboBox%26quot%3B%29">HostComponent("ComboBox")</a>
        ]]>
    </fx:Metadata>

    <s:states>
        <s:State name="normal" />
        <s:State name="open" />
        <s:State name="disabled" />
    </s:states>

    <!--- 
    The PopUpAnchor control that opens the drop-down list. 
    -->
    <s:PopUpAnchor id="popUp"  displayPopUp.normal="false" displayPopUp.open="true" includeIn="open"
                   left="0" right="0" top="0" bottom="0" itemDestructionPolicy="auto"
                   popUpPosition="below" popUpWidthMatchesAnchorWidth="true">

        <!--- 
        The drop down area of the skin. 
        This includes borders, background colors, scrollers, and filters. 
        -->
        <s:Group id="dropDown" maxHeight="134" minHeight="22" >

            <!-- drop shadow -->
            <s:RectangularDropShadow blurX="20" blurY="20" alpha="1.0" distance="5" 
                                     angle="90" color="#000000" left="0" top="0" right="0" bottom="0"/>

            <!-- border -->
            <s:Rect left="0" right="0" top="0" bottom="0">
                <s:stroke>
                    <s:SolidColorStroke color="0x686868" weight="1"/>
                </s:stroke>
            </s:Rect>

            <!-- fill -->
            <!---  
            Defines the appearance of drop-down list's background fill.
            -->
            <s:Rect id="background" left="1" right="1" top="1" bottom="1" >
                <s:fill>
                    <!---  
                    The color of the drop down's background fill.
                    The default color is 0xFFFFFF.
                    -->
                    <s:SolidColor id="bgFill" color="0xFFFFFF" />
                </s:fill>
            </s:Rect>

            <s:Scroller left="0" top="0" right="0" bottom="0" focusEnabled="false" minViewportInset="1">
                <!---  
                The container for the data items in the drop-down list.
                -->
                <s:DataGroup id="dataGroup" itemRenderer="spark.skins.spark.DefaultItemRenderer">
                    <s:layout>
                        <s:VerticalLayout gap="0" horizontalAlign="contentJustify"/>
                    </s:layout>
                </s:DataGroup>
            </s:Scroller>
        </s:Group>
    </s:PopUpAnchor>

    <!--- The anchor button used by the DropDownList. The default skin is DropDownListButtonSkin. -->
    <s:Button id="openButton" width="20" right="0" top="0" bottom="0" focusEnabled="false"
              skinClass="ComboBoxButtonSkin" />  
    <!--- The prompt area of the DropDownList. -->
    <mx:TextInput id="inputField" textAlign="left"
                  left="0" right="19" top="0" bottom="0" />

</s:Skin>

\

<fx:Style>
    @namespace s "library://ns.adobe.com/flex/spark";

    s|ComboBox s|TextInput
    {
        borderAlpha : .8;
        borderColor : #00FF00;
    }
</fx:Style>

<s:ComboBox/>

Additional Implementation Details


Enter implementation/design details for the feature here. This section may be updated after the spec signs off.

Prototype Work


There are two prototypes for ComboBox, one for highlighting the input string matching item and one for only showing the items that match the input string.

Compiler Work


None

Web Tier Compiler Impact


None

Flex Feature Dependencies


None

Backwards Compatibility


Syntax changes

None

Behavior

None

Warnings/Deprecation

None

Accessibility


ComboBox will need to be made accessible. It is a complex component with lots of user interactions and states.

Performance


How expensive is it to filter or match to a large data set? Will filtering or matching work with virtualLayout?

Globalization


None

Localization


Compiler Features

None

Framework Features

None

Issues and Recommendations


Open Issues

Resolved Issues

Should we proxy the TextInput properties?

We can either expose the TextInput properties in ComboBox, or require the developer to set these properties directly on the TextInput in the ComboBox skin. The only properties I can see proxying are restrict and maxChars.

Should we proxy the TextInput styles?

Most of the TextInput styles are inheriting, so setting the styles on the ComboBox will be applied to the TextInput. For non-inheriting styles, should these just be set directly in the ComboBox skin?

Should we expose/redispatch TextInput events?

TextInput dispatches change, changing, enter, and selectionChange. None of these events bubble, so they would have to be redispatched by the ComboBox. The change and changing events are already dispatched by ListBase, but have a different event type from the TextInput ones. Should we change the change and changing to textChange and textChanging events? The Halo ComboBox dispatches a change event for both events.

Do we expose the TextInput.text as a property?

We can add a property called inputString which grabs the value from the TextInput's text property. The advantage of this property is that it makes it clear how to obtain the value (instead of having to go through the textInput skin part. It also makes it easier to read and write to this value before the component has been initialized. The value will be locally stored until the textInput part is added).

On the other hand, textInput is a public property and it should be fairly easy for developers to read/write from it granted they wait until the part has been added.

Should ComboBox subclass DropDownListBase or TextInput

It will subclass DropDownListBase. There are far more properties and styles to support for DropDownListBase than TextInput. ComboBox reuses much of the functionality and implementation of DropDownListBase and its base classes.

Documentation


Describe any documentation issues or any tips for the doc team.

QA


If there are testing tips for QA, note them here, include a link to the test plan document.

TODO: update Flex Builder QA section.


Related

Wiki: Flex 4

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.