Skip to main content
Participant
October 9, 2009
Question

ComboBox (ComboBase) incompatibilities in Flex 4 SDK

  • October 9, 2009
  • 3 replies
  • 4966 views

Hi,

I'm using an autocomplete component which extends ComboBox. Unfortunately it no longer compiles under the Flex 4 SDK. The problem is that ComboBase defines:

protected var textInput:ITextInput;

which used to be a simple TextInput. The problem now is that textInput does not have setSelection or selectionBeginIndex properties.

Any ideas how to get around this ?

Thanks,

Grant.

This topic has been closed for replies.

3 replies

Participant
October 14, 2009

Have you seen this yet.

Spark Text Primitives Decision

http://opensource.adobe.com/wiki/display/flexsdk/Spark+Text+Primitives+Decision

I was having a similar probelm.  I replaced all my SimpleText with Label and then changed the TextInput.setSelection() to TextInput.selectRange().

Participant
October 21, 2009

The bigger problem is textInput.selectionBeginIndex. How do you get to that in the new scheme of things ?

Inspiring
October 12, 2009

I ran into this a couple weeks ago and brought it up to a couple of team members.

This is a change that was introduced post Beta 1 of the Flex 4 SDK.  So you may be able to roll back to that version depending what other features you are using.

But with MAX last week and Adobe shutdown this week you wont likely hear back on this.

I tried changing up the Interface declaration and casting the interface but all was a failure.

So hopefully when everyone gets back from vacation we can get this resolved.

Participant
October 21, 2009

Is there a bug report for this somewhere ? I can't seem to find it.

Participant
October 12, 2009

If it will help, here is the Autocomplete component I'm using. Again, It compiles perfectly in SDK 3.4, and even some early 4.0 builds, but not recent 4.0 builds:

////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2003-2006 Adobe Macromedia Software LLC and its licensors.
//  All Rights Reserved. The following is Source Code and is subject to all
//  restrictions on such code as contained in the End User License Agreement
//  accompanying this product.
//
////////////////////////////////////////////////////////////////////////////////

package com.marathon.flexcon.view.custom
{
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.net.SharedObject;
import flash.ui.Keyboard;

import mx.controls.ComboBox;
import mx.core.UIComponent;
import mx.collections.ArrayCollection;
import mx.collections.ListCollectionView;
import mx.collections.IViewCursor;
import mx.utils.ObjectUtil;




//--------------------------------------
//  Events
//--------------------------------------

/**
*  Dispatched when the <code>filterFunction</code> property changes.
*
*  You can listen for this event and update the component
*  when the <code>filterFunction</code> property changes.</p>
*
*  @eventType flash.events.Event
*/
[Event(name="filterFunctionChange", type="flash.events.Event")]

/**
*  Dispatched when the <code>typedText</code> property changes.
*
*  You can listen for this event and update the component
*  when the <code>typedText</code> property changes.</p>
*
*  @eventType flash.events.Event
*/
[Event(name="typedTextChange", type="flash.events.Event")]

//--------------------------------------
//  Excluded APIs
//--------------------------------------

[Exclude(name="editable", kind="property")]

/**
*  The AutoComplete control is an enhanced
*  TextInput control which pops up a list of suggestions
*  based on characters entered by the user. These suggestions
*  are to be provided by setting the <code>dataProvider
*  </code> property of the control.
*  @mxml
*
*  <p>The <code>&lt;fc:AutoComplete&gt;</code> tag inherits all the tag attributes
*  of its superclass, and adds the following tag attributes:</p>
*
*  <pre>
*  &lt;fc:AutoComplete
*    <b>Properties</b>
*    keepLocalHistory="false"
*    lookAhead="false"
*    typedText=""
*    filterFunction="<i>Internal filter function</i>"
*
*    <b>Events</b>
*    filterFunctionChange="<i>No default</i>"
*    typedTextChange="<i>No default</i>"
*  /&gt;
*  </pre>
*
*  @includeExample ../../../../../../docs/com/adobe/flex/extras/controls/example/AutoCompleteCountriesData/AutoCompleteCountriesData.mxml
*
*  @see mx.controls.ComboBox
*
*/
public class AutoComplete extends ComboBox
{

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor.
     */
    public function AutoComplete()
    {
        super();

        //Make ComboBox look like a normal text field
        editable = true;
        if(keepLocalHistory)
            addEventListener("focusOut",focusOutHandler);

        setStyle("arrowButtonWidth",0);
        setStyle("fontWeight","normal");
        setStyle("cornerRadius",0);
        setStyle("paddingLeft",0);
        setStyle("paddingRight",0);
        rowCount = 7;
    }
   
    // GS Added this for correct object selection
    override public function set selectedItem(value:Object):void {
        super.selectedItem = value;
        if (value != null && selectedIndex == -1) {
            // do a painful search;
            if (collection && collection.length) {
                var cursor:IViewCursor = collection.createCursor();
                while (!cursor.afterLast) {
                    var obj:Object = cursor.current;
                    if ( ObjectUtil.compare( obj, value ) == 0 ) {
                        super.selectedItem = obj;
                        return;
                    }
                    cursor.moveNext();
                }
            }
        }
    }
   
      /**
      *  Closes the combox and set the selection which is lost using Flex 3
      *
      *  @event  Event Trigger event to close the combobox
      */    
      override public function close(event:Event = null):void
      {
          super.close(event);
        
          if(selectedIndex == 0)
          {
              // set the text using the selected label
              textInput.text = selectedLabel;
              // select the text from typed text position to texts length
              textInput.setSelection(cursorPosition, textInput.text.length);     
          }
      }



    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private var cursorPosition:Number=0;

    /**
     *  @private
     */
    private var prevIndex:Number = -1;

    /**
     *  @private
     */
    private var removeHighlight:Boolean = false;   

    /**
     *  @private
     */
    private var showDropdown:Boolean=false;

    /**
     *  @private
     */
    private var showingDropdown:Boolean=false;

    /**
     *  @private
     */
    private var tempCollection:Object;

    /**
     *  @private
     */
    private var usingLocalHistory:Boolean=false;
   
    /**
     *  @private
     */
    private var dropdownClosed:Boolean=true;

    //--------------------------------------------------------------------------
    //
    //  Overridden Properties
    //
    //--------------------------------------------------------------------------

     //----------------------------------
    //  editable
    //----------------------------------
    /**
     *  @private
     */
     override public function set editable(value:Boolean):void
    {
        //This is done to prevent user from resetting the value to false
        super.editable = true;
    }
    /**
     *  @private
     */
     override public function set dataProvider(value:Object):void
    {
        super.dataProvider = value;
        if(!usingLocalHistory)
            tempCollection = value;
    }

    //----------------------------------
    //  labelField
    //----------------------------------
    /**
     *  @private
     */
     override public function set labelField(value:String):void
    {
        super.labelField = value;
       
        invalidateProperties();
        invalidateDisplayList();
    }


    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------


    //----------------------------------
    //  filterFunction
    //----------------------------------

    /**
     *  @private
     *  Storage for the filterFunction property.
     */
    private var _filterFunction:Function = defaultFilterFunction;

    /**
     *  @private
     */
    private var filterFunctionChanged:Boolean = true;

    [Bindable("filterFunctionChange")]
    [Inspectable(category="General")]

    /**
     *  A function that is used to select items that match the
     *  function's criteria.
     *  A filterFunction is expected to have the following signature:
     *
     *  <pre>f(item:~~, text:String):Boolean</pre>
     *
     *  where the return value is <code>true</code> if the specified item
     *  should displayed as a suggestion.
     *  Whenever there is a change in text in the AutoComplete control, this
     *  filterFunction is run on each item in the <code>dataProvider</code>.
     * 
      *  <p>The default implementation for filterFunction works as follows:<br>
      *  If "AB" has been typed, it will display all the items matching
     *  "AB~~" (ABaa, ABcc, abAc etc.).</p>
     *
     *  <p>An example usage of a customized filterFunction is when text typed
     *  is a regular expression and we want to display all the
     *  items which come in the set.</p>
     *
     *  @example
     *  <pre>
     *  public function myFilterFunction(item:~~, text:String):Boolean
     *  {
     *     public var regExp:RegExp = new RegExp(text,"");
     *     return regExp.test(item);
     *  }
     *  </pre>
     *
     */
    public function get filterFunction():Function
    {
        return _filterFunction;
    }

    /**
     *  @private
     */
    public function set filterFunction(value:Function):void
    {
        //An empty filterFunction is allowed but not a null filterFunction
        if(value!=null)
        {
            _filterFunction = value;
            filterFunctionChanged = true;

            invalidateProperties();
            invalidateDisplayList();
   
            dispatchEvent(new Event("filterFunctionChange"));
        }
        else
            _filterFunction = defaultFilterFunction;
    }

    //----------------------------------
    //  filterFunction
    //----------------------------------

    /**
     *  @private
     *  Storage for the keepLocalHistory property.
     */
    private var _keepLocalHistory:Boolean = false;

    /**
     *  @private
     */
    private var keepLocalHistoryChanged:Boolean = true;

    [Bindable("keepLocalHistoryChange")]
    [Inspectable(category="General")]

    /**
     *  When true, this causes the control to keep track of the
     *  entries that are typed into the control, and saves the
     *  history as a local shared object. When true, the
     *  completionFunction and dataProvider are ignored.
     *
     *  @default "false"
     */
    public function get keepLocalHistory():Boolean
    {
        return _keepLocalHistory;
    }

    /**
     *  @private
     */
    public function set keepLocalHistory(value:Boolean):void
    {
        _keepLocalHistory = value;
    }

    //----------------------------------
    //  lookAhead
    //----------------------------------

    /**
     *  @private
     *  Storage for the lookAhead property.
     */
    private var _lookAhead:Boolean=false;

    /**
     *  @private
     */
    private var lookAheadChanged:Boolean;

    [Bindable("lookAheadChange")]
    [Inspectable(category="Data")]

    /**
     *  lookAhead decides whether to auto complete the text in the text field
     *  with the first item in the drop down list or not.
     *
     *  @default "false"
     */
    public function get lookAhead():Boolean
    {
        return _lookAhead;
    }

    /**
     *  @private
     */
    public function set lookAhead(value:Boolean):void
    {
        _lookAhead = value;
        lookAheadChanged = true;
    }

    //----------------------------------
    //  typedText
    //----------------------------------

    /**
     *  @private
     *  Storage for the typedText property.
     */
    private var _typedText:String="";
    /**
     *  @private
     */
    private var typedTextChanged:Boolean;

    [Bindable("typedTextChange")]
    [Inspectable(category="Data")]

    /**
     *  A String to keep track of the text changed as
     *  a result of user interaction.
     */
    public function get typedText():String
    {
        return _typedText;
    }

    /**
     *  @private
     */
    public function set typedText(input:String):void
    {
        _typedText = input;
        typedTextChanged = true;
           
        invalidateProperties();
        invalidateDisplayList();
        dispatchEvent(new Event("typedTextChange"));
    }

    //--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function commitProperties():void
    {
        super.commitProperties();
       
          if(!dropdown)
            selectedIndex=-1;
             
        if(dropdown)
        {
            if(typedTextChanged)
            {
                cursorPosition = textInput.selectionBeginIndex;

                updateDataProvider();

                //In case there are no suggestions there is no need to show the dropdown
                  if(collection.length==0 || typedText==""|| typedText==null)
                  {
                      dropdownClosed=true;
                    showDropdown=false;
                  }
                else
                {
                    showDropdown = true;
                    selectedIndex = 0;
                 }
             }
        }
    }
       
    /**
     *  @private
     */
    override protected function focusOutHandler(event:FocusEvent):void
    {
        super.focusOutHandler(event)
         if(keepLocalHistory && dataProvider.length==0)
             addToLocalHistory();
    }
    /**
     *  @private
     */
    override public function getStyle(styleProp:String):*
    {
        if(styleProp != "openDuration")
            return super.getStyle(styleProp);
        else
        {
             if(dropdownClosed)
                 return super.getStyle(styleProp);
             else
                 return 0;
        }
    }
    /**
     *  @private
     */
    override protected function keyDownHandler(event:KeyboardEvent):void
    {
        super.keyDownHandler(event);

        if(!event.ctrlKey)
        {
            //An UP "keydown" event on the top-most item in the drop-down
            //or an ESCAPE "keydown" event should change the text in text
            // field to original text
            if(event.keyCode == Keyboard.UP && prevIndex==0)
            {
                 textInput.text = _typedText;
                textInput.setSelection(textInput.text.length, textInput.text.length);
                selectedIndex = -1;
            }
            else if(event.keyCode==Keyboard.ESCAPE && showingDropdown)
            {
                 textInput.text = _typedText;
                textInput.setSelection(textInput.text.length, textInput.text.length);
                showingDropdown = false;
                dropdownClosed=true;
            }
            else if(event.keyCode == Keyboard.ENTER)
            {
                 if(keepLocalHistory && dataProvider.length==0)
                    addToLocalHistory();
             }
            else if(lookAhead && event.keyCode ==  Keyboard.BACKSPACE
            || event.keyCode == Keyboard.DELETE)
                removeHighlight = true;
         }
         else
            if(event.ctrlKey && event.keyCode == Keyboard.UP)
                dropdownClosed=true;
        
         prevIndex = selectedIndex;
    }
   
    /**
     *  @private
     */
    override protected function measure():void
    {
        super.measure();
        measuredWidth = mx.core.UIComponent.DEFAULT_MEASURED_WIDTH;
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number,
                              unscaledHeight:Number):void
    {
       
        super.updateDisplayList(unscaledWidth, unscaledHeight);
       
        //An UP "keydown" event on the top-most item in the drop
        //down list otherwise changes the text in the text field to ""
          if(selectedIndex == -1)
            textInput.text = typedText;

        if(dropdown)
        {
            if(typedTextChanged)
            {
                //This is needed because a call to super.updateDisplayList() set the text
                // in the textInput to "" and thus the value
                //typed by the user losts
                  if(lookAhead && showDropdown && typedText!="" && !removeHighlight)
                {
                    var label:String = itemToLabel(collection[0]);
                    var index:Number =  label.toLowerCase().indexOf(_typedText.toLowerCase());
                    if(index==0)
                    {
                        textInput.text = _typedText+label.substr(_typedText.length);
                        textInput.setSelection(textInput.text.length,_typedText.length);
                    }
                    else
                    {
                        textInput.text = _typedText;
                        textInput.setSelection(cursorPosition, cursorPosition);
                        removeHighlight = false;
                    }
                       
                }
                else
                {
                    textInput.text = _typedText;
                    textInput.setSelection(cursorPosition, cursorPosition);
                    removeHighlight = false;
                }
               
                typedTextChanged= false;
            }
            else if(typedText)
                //Sets the selection when user navigates the suggestion list through
                //arrows keys.
                textInput.setSelection(_typedText.length,textInput.text.length);
        }
         if(showDropdown && !dropdown.visible)
         {
             //This is needed to control the open duration of the dropdown
             super.open();
            showDropdown = false;
             showingDropdown = true;

             if(dropdownClosed)
                 dropdownClosed=false;
         }
    }
   

    /**
     *  @private
     */
    override protected function textInput_changeHandler(event:Event):void
    {
        super.textInput_changeHandler(event);
        //Stores the text typed by the user in a variable
        typedText=text;
    }

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------
               
    /**
     *  @private
     *  If keepLocalHistory is enabled, stores the text typed
     *     by the user in the local history on the client machine
     */
    private function addToLocalHistory():void
    {
        if (id != null && id != "" && text != null && text != "")
        {
            var so:SharedObject = SharedObject.getLocal("AutoCompleteData");

            var savedData : Array = so.data.suggestions;
            //No shared object has been created so far
            if (savedData == null)
                savedData = new Array();

             var i:Number=0;
             var flag:Boolean=false;
             //Check if this entry is there in the previously saved shared object data
             for(i=0;i<savedData.length;i++)
                if(savedData==text)
                {
                    flag=true;
                    break;
                }
             if(!flag)
             {
                 //Also ensure it is not there in the dataProvider
                 for(i=0;i<collection.length;i++)
                     if(defaultFilterFunction(itemToLabel(ListCollectionView(collection).getItemAt(i)),text))
                    {
                        flag=true;
                        break;
                    }
             }
             if(!flag)
                 savedData.push(text);

           so.data.suggestions = savedData;
           //write the shared object in the .sol file
           so.flush();
        }
    }   
    /**
     *  @private
     */
    private function defaultFilterFunction(element:*, text:String):Boolean
    {
        var label:String = itemToLabel(element);
        return (label.toLowerCase().substring(0,text.length) == text.toLowerCase());
    }
    /**
     *  @private
     */

     private function templateFilterFunction(element:*):Boolean
    {
        var flag:Boolean=false;
        if(filterFunction!=null)
            flag=filterFunction(element,typedText);
        return flag;
    }

    /**
     *  @private
     *  Updates the dataProvider used for showing suggestions
     */
    private function updateDataProvider():void
    {
        dataProvider = tempCollection;
         collection.filterFunction = templateFilterFunction;
        collection.refresh();

        //In case there are no suggestions, check there is something in the localHistory
          if(collection.length==0 && keepLocalHistory)
          {
            var so:SharedObject = SharedObject.getLocal("AutoCompleteData");
            usingLocalHistory = true;
            dataProvider = so.data.suggestions;
            usingLocalHistory = false;
             collection.filterFunction = templateFilterFunction;
            collection.refresh();
          }
      }
}   
}