Applying selection dynamically to Spark RichText component

February 4th, 2010

Flex 4 documentation article named “Selecting and modifying text” lists an finite final list of components that supports the selection:

  • RichEditableText
  • Label (Spark only)
  • TextInput (both MX and Spark)
  • TextArea (both MX and Spark)
  • RichTextEditor and all controls that have a TextArea as a subcomponent

   This list does not include RichText component, but luckily with a new Text Layout Framework (TLF) available starting from Flash player 10, it is quite easy to “simulate” dynamic selection of the text with some ActionScript 3 code.

   In the following example, try to drag the slider to the left and to the right to control the dynamic selection of the text of RichText component:

Get Adobe Flash player

Code listing:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/halo"
               width="200"
               height="75">
    <s:layout>
        <s:HorizontalLayout paddingLeft="25"
                            paddingTop="25"
                            paddingRight="25"/>
    </s:layout>
    <fx:Script>
        <![CDATA[            
            import flashx.textLayout.edit.EditManager;
            import flashx.textLayout.edit.SelectionState;
            import flashx.textLayout.formats.TextLayoutFormat;            
 
            protected function highlightItem(endSelectionCharIndex : int):void
            {
                var containerFormat:TextLayoutFormat = new TextLayoutFormat();                
                var paragraphFormat:TextLayoutFormat = new TextLayoutFormat();                
                var characterFormat:TextLayoutFormat = new TextLayoutFormat();
 
                characterFormat = _selectedTextFormat;
 
                var selectionState : SelectionState = new SelectionState(rt.textFlow, 0, endSelectionCharIndex, _selectedTextFormat);                
 
                // apply format to the selection
                _textEditManager.applyFormat(
                    characterFormat, 
                    paragraphFormat, 
                    containerFormat, 
                    selectionState);                                
 
                characterFormat = _notSelectedTextFormat;                
 
                // apply format to the rest of the text
                var notSelectionState : SelectionState = new SelectionState(rt.textFlow, endSelectionCharIndex, rt.text.length, _notSelectedTextFormat);                
                _textEditManager.applyFormat(
                    characterFormat, paragraphFormat, containerFormat, notSelectionState);
            }
 
 
            protected function onRichTextCreationComplete():void
            {
                _textEditManager = new EditManager();
                rt.textFlow.interactionManager = _textEditManager;
 
                _selectedTextFormat = new TextLayoutFormat();
                _selectedTextFormat.backgroundColor = 0xFF99CC;
                _selectedTextFormat.color = 0x000000;
 
                _notSelectedTextFormat = new TextLayoutFormat();
                _notSelectedTextFormat.backgroundColor = 0xFFFFFF;
                _notSelectedTextFormat.color = 0x000000;
            }
 
            private var _textEditManager : EditManager;
 
            private var _selectedTextFormat : TextLayoutFormat;
 
            private var _notSelectedTextFormat : TextLayoutFormat;            
 
        ]]>
    </fx:Script>                   
    <s:RichText id="rt" text="Sample text" 
                creationComplete="onRichTextCreationComplete()"/>
    <s:HSlider id="slider" 
              minimum="0" 
              maximum="10" 
              change="highlightItem(slider.value)">        
    </s:HSlider>        
</s:Application>

When overriding “commitProperties” put “super.commitProperties” call to the end of the method

February 3rd, 2010

My use-case
   Recently I’ve tried to introduce skin states to the pretty complex UI component in Adobe Flex 4. I have got puzzled by the behaviour that my invalidateSkinState() calls never had triggered execution of getCurrentSkinState method that controlls the current UI component skin state value as expected.

[pullquote]Invalidation of the same phase while processing that phase is ignored – by Alex Harui[/pullquote]

   Under closer examination I found out that I fall into the same invalidation trap that James Polanco did in November 2010.

   I must stress out that it was easy to fall into this trap in my case because inside “commitProperties” call I was doing pretty complex calculations that involved, for example, the creation of display object sub-children, listening for “preinitialize” events from those sub-children, reacting to it, etc.

    To cut the talk short, to prevent this property invalidation trap from happening I suggest to always follow the simple rule of the thumb:

When overriding “commitProperties” put “super.commitProperties” call to the end of the method

override protected function commitProperties():void
{
    if (myPropertyChanged)
    {        
        disableMyUIComponent = true;
        invalidateSkinState();
        myPropertyChanged = false;
    }
    super.commitProperties();
}
 
override protected function getCurrentSkinState():String { 
    var returnState:String = "normal"; 
 
    // Use information in the class to determine the new view state of the skin class. 
    if (disableMyUIComponent)     { 
        returnState = "disabled"; 
    } 
    return returnState; 
}

Futher reading:
Discussion at 2008 at Flexcoders mailing list (yeah, at that time, this mailing list was more interesting to read) “Must call super.commitProperties at END of overrided commitProperties when extending XXXX?”