Applying selection dynamically to Spark RichText component

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>

Comments

When it is time to reset dirty flag “invalidatePropertiesFlag” in Flex invalidation framework?

   Invalidation in Flex is a mechanism by which changes made to a component's property values are queued and processed.This property value invalidation in Flex is controlled by setting and resetting boolean variables called dirty flags.

   One of such a dirty flags used internally in Flex invalidation mechanism is invalidatePropertiesFlag flag.

   This particular flag is reset in validateProperties() method from mx.core.UIComponent class from Flex 3 SDK:

public function validateProperties():void
{
    if (invalidatePropertiesFlag)
    {
        commitProperties();
        invalidatePropertiesFlag = false;
    }
}

Inside commitProperties() function the actual changes occur to the invalidated property's value, for example, it can be "width" property.

My question is:

   Why dirty flag invalidatePropertiesFlag is reset after commitProperties() call, but not before this call?

I speculate this happens because:

  • It is just a matter of life == code convention to put the dirty flag at the end of IF block;
  • If code within commitProperties() raises RTE, we will still have a chance to execute the code in commitProperties() during playing next frame in Flash Player and this time maybe the code will not throw RTE.

   At this point, you may start to wonder why the exact location (at the very beginning or at the very end of IF sequence) of the dirty flag can be important?

__('Read the rest of this entry »')

Comments

NEVER use selectable=false for editable List or DataGrid Flex components

It is very likely that the authors of Adobe Flex SDK have never devised the following simple use-case for both List and DataGrid components from Flex SDK:

  • "selectable" property is set to false
  • "editable" property is set to true

    To prove this fact, try to play with the following samples:

    mx:List

    Get Adobe Flash player

    Click the first row, in a List, then the itemEditor appears on the first row.
    Click the second row in a List , but the itemEditor doesn't appear on the second row

    That's bug SDK-15309, documented over year ago.

    mx:DataGrid

    Get Adobe Flash player

    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                    creationComplete="initApp();"
                    width="400"
                    height="300"
                    styleName="plain"
                    paddingLeft="0"
                    paddingRight="0"
                    paddingTop="0"
                    paddingBottom="0">
    
        <mx:Script>
            <![CDATA[
                import mx.events.DataGridEvent;
    
                private function initApp():void {
                    dg.dataProvider =                     [
                        {Artist:'Carole King', Album:'Tapestry', Price:11.99},
                        {Artist:'Paul Simon', Album:'Graceland', Price:10.99},
                        {Artist:'Original Cast', Album:'Camelot', Price:12.99},
                        {Artist:'The Beatles', Album:'The White Album', Price:11.99}
                        ];
                }
    
                private function onItemEditBegin(event : DataGridEvent) : void
                {
                    trace ("DG onItemEditBegin");
                }
    
            ]]>
        </mx:Script>
        <mx:DataGrid id="dg"
                     rowHeight="20"
                     editable="true"
                     selectable="false"
                     width="100%"
                     height="100%"
                     itemEditBegin="onItemEditBegin(event)">
            <mx:columns>
                <mx:DataGridColumn
                                   dataField="Artist"
                                   width="100">
                </mx:DataGridColumn>
                <mx:DataGridColumn
                                   dataField="Album"/>
                <mx:DataGridColumn
                                   dataField="Price"/>
            </mx:columns>
        </mx:DataGrid>
    </mx:Application>
    

    Now, try to click on the DataGrid header, oops, you've just hit SDK-19436 bug (itemEditor instance is created unwillingly at first column, first row of DataGrid).

    Then try to scroll the DataGrid with scroller buttons, and again you've hit another, albeit similar, SDK-21726 bug (itemEditor instance is created unwillingly at first column, first row of DataGrid).

    Now scroll the contents of Datagrid a little bit down with a scroller thumb and start to edit cell in the first topmost visible row, first column, now move the current focus by pressing keyboard combination Shift+Tab, and once again, you've hit a SDK-16262 bug.

    To conclude this post: do not set selectable on mx:List or mx:DataGrid components to false if you are going to edit their values using itemEditors, until all above mentioned bugs will be fixed.

  • Comments (2)

    Always keep an eye on most recent stable release of Adobe Flex SDK

       I was going to enter a new issue into Adobe JIRA Flex SDK bugbase that I could confirm to be present in the most recent milestone Adobe Flex SDK release 3.1.0.2710, shipped together with an August 2008 Flex Builder 3.0.1 update.

       Just before submitting the bug issue, I've tested the functionality under the question under the latest Adobe Flex SDK stable build (3.2.0.3794 at the time of writing) - and I was surprised to find out that the bug is already fixed. Joy! Joy!

        I rushed to find the reasoning for this change for UITextFormat class by reading change notes at Flex SDK download section at Adobe open source web-site, but could not find any remarks regarding this change - would be handy to have a better insight behind this change for the community.

       ps If you wonder, the issue was incorrect implementation of an internal function copyFrom from UITextFormat class. UITextField's public getUITextFormat() method relies on this copyFrom function to return UITextFormat object for the UITextField class instance. In a nutshell UITextFormat object is just a wrapper around textFormat class that should contain all the properties from wrapped textFormat class instance + add some additional functionality.

       As it turned out, In Adobe Flex SDK 3.1 and below, 7(!) different properties were not copied into UITextFormat object from existing textFormat format object because of wrong implementation of UITextFormat.copyFrom function.

       Now, we are safe, this particular issue is fixed!

    Comments

    Hint: Always better to use ‘itemUpdated’ method instead of ’setItemAt’ when updating existing items in ArrayCollection

       When you want to update already existing data item in your ArrayCollection in ActionScript 3 with a new values for object's properties you have 2 options to select from:

    1. use itemUpdated(obj) ArrayCollection's method.
    2. use setItemAt(obj, index) ArrayCollection's method.

       ActionScript code sample:

        //Update an existing person in the ArrayCollection via setItemAt
    
        public function updatePersonViaSetItemAt():void {
            var currentlySelectedItem : Object = dg.selectedItem;
            currentlySelectedItem.first = firstInput.text;
            currentlySelectedItem.last = lastInput.text;
            ac.setItemAt(currentlySelectedItem, dg.selectedIndex);
        }
    
        // Update an existing person in the ArrayCollection via itemUpdated
    
        public function updatePersonViaItemUpdated():void {
            var currentlySelectedItem : Object = dg.selectedItem;
            currentlySelectedItem.first =  firstInput.text;
            currentlySelectedItem.last =  lastInput.text;
            ac.itemUpdated(currentlySelectedItem);
    
        }
    

       Which one to choose?

       I would strongly advice to always use 'itemUpdated' because of the following reasons:

    • Calling setItemAt(x) on your existing data item in ArrayCollection is an equivalent to calling the removeItemAt(x) method and then calling the addItemAt(..., x) method on your data item.

         Thus after making the call to setItemAt(x, index) you will loose the current selection in your ArrayCollection dependant UI component (mx:DataGrid, mx:Tree, mx:Combobox.. etc..)

    • If the Sort rule is already applied to your ArrayCollection then you can receive some funky results like unwanted inserting of a new row when partly changing several properties of the existing data item in ArrayCollection.

       Interactive sample with "View source" option enabled to watch the difference:
    http://jabbypanda.com/labs/updateArrayCollectionItem/updateArrayCollectionItem.html

    You can try the following:

    1. First apply sorting to any of the DataGrid's column
    2. Select the first row in the Datagrid
    3. Rename the surname of the person to something completely new starting with a new letter
    4. Press "Update via setItemAt()" button control

    Voilà, new unwanted row becomes visible in the Datagrid.

    Comments (1)

    The first meeting of Ukrainian Adobe Flash platform user group (UAFPUG) went well!

    Kharkiv, Ukraine is quickly becoming a popular place for Ukrainian Flash and Flex developers to meet.

    After intoductory meeting of Ukrainian Adobe Flash platform user group(UAFPUG) at February 2008 codenamed UAFPUG-0, the first real seminar UAFPUG-1 had quickly followed at 15th of March, 2008 at Global Logic office premises at Kharkiv, Ukraine.

    Speakers list had included 3 (THREE!) talented local Flash profesionals who had presented to the audience of 45 attendees mostly from all major cities in Eastern Ukraine and Kyiv included + 1 international visitor from Russia.

    The speech topics were as follows:

  • Event flow model in ActionScript 3.0 (presented by Pirrest)
  • Introducing Flash media server 3.0 feature (presented by Dinosaur)
  • Applying PureMVC framework to the creation of vector map of Moscow city (like Mos2.ru does) (presented by _rost)
  • .
    UAFPUG-1 meeting
    Rostyslav Syrik _rost discusses the ins and outs of PureMVC framework.

    UAFPUG-1 meeting audience
    The UAFPUG-1 meeting audience was very into the subject of the seminars :) .

    Thank you to all of you, who had helped to make this meeting to become a reality, especially _rost, Reijji, __i, Pirrest and Global Logic team crew.

    Comments (2)

    How to hide the unwanted content visible outside of mx:SWFLoader boundaries

    The issue:

    I was stumbled upon once ago when I loaded my external SWF file authored by the Flash IDE by the help of component and suddenly (?) every content object that was originally placed outside of the Stage inside the Flash IDE became visible in my Flex 2 based web application outside of the boundaries after loading sequence for my external SWF file was over (the file is Flash8 based, AVM1 if this matters).

    The corresponding task in Adobe JIRA bug-database:
    http://bugs.adobe.com/jira/browse/SDK-14590

    I currently fight this issue by applying the mask over the content that is loaded by the component, see the code below, may be you can find it helpful too:

    <?xml version="1.0" encoding="utf-8"?>
    <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
        resize="applyMask()" >
    
        <mx:Script>
            <![CDATA[
                private function applyMask() : void {
                    if (swf != null &&
                        swf.content != null &&
                        swf.content.loaderInfo.width > 0 &&
                        swf.content.loaderInfo.height > 0) {
                        var scaleRatioX : Number = swf.content.loaderInfo.width /
                                                        this.width;
                        var scaleRatioY : Number = swf.content.loaderInfo.height /
                                                       (this.height - this.controlBar.height);
                        var scaleRatio : Number = Math.max(scaleRatioX, scaleRatioY);
    
                        swf.width = swf.content.loaderInfo.width > 0 ?
                              Math.round(swf.content.loaderInfo.width / scaleRatio)  : 100;
                        swf.height = swf.content.loaderInfo.height > 0 ?
                              Math.round(swf.content.loaderInfo.height / scaleRatio) : 100;
    
                        var s : Shape = new Shape();
                            s.graphics.beginFill(0xFFFFFF);
                         s.graphics.drawRect(0, 0,  swf.width, swf.height);
                         s.graphics.endFill();
                         swf.addChild(s);
                         s.visible = false;
                         swf.mask = s;
                    }
                }
        ]]>
        </mx:Script>
        <mx:SWFLoader id="swf"
            complete="applyMask()"
            source="navigation.swf"/>
    </mx:VBox>
    

    Comments (2)

    Incompatibility issue when migrating Flex SWC Library projects from Flex 2 Builder to Builder 3 Beta 2 project and back

    The steps I underwent:

    1. I had installed Flex Builder 3 beta 2 (FB 3 beta 2) as a plugin on my Eclipse 3.2
    2. Opened my Flex SWF Library projects inside of Flex Builder 3 beta 2
    3. Selected Flex 3 M3 (Beta 2) SDK at the dialog (default value)
    4. Flex Buidler 2 had given me the following error in my log file: (full error info skipped for the sake of text clarity)
    5. java.lang.IllegalArgumentException: Attempted to beginRule: P/FlexLibrary, does not match outer scope rule: P/as3corelib
      at com.adobe.flexbuilder.project.actionscript.internal.FlexProjectPreferences.setUpgradeFlexSDK
      (FlexProjectPreferences.java:147)
      at com.adobe.flexbuilder.project.actionscript.internal.ActionScriptProjectSettings$2.runInUIThread
      (ActionScriptProjectSettings.java:285)

    6. Nevertheless of the received error, I was able to compile ALL my Flex 2 based projects that relies on my Flex SWC Library projects in FB 3 Beta 2
    7. Then I had reopened my plain Flex projects inside Flex Builder 2 again, because at the company we are using currently Flex Builder 2 for the production.
    8. But Flex Builder 2 constantly had failed to recompile all Flex SWC Library projects that were previously opened at least once at FB 3 beta2.

    My resolution to this issue was to delete manually a config file created by FB 3 beta 2 and located at each Flex SWC library project folder at the following path:

    c:\workspace\\.settings\com.adobe.flexbuilder.project.prefs

    Comments (1)

    How big is your Flex?

    Mine Flex 2 authored SWF is 19 levels deep in its full glory:

    _level0.index0.VBox5.GradientBox25.indexViewStack.
    mainPage.contentPane.mainViewStack.customerViewStack.
    presentationViewer.swf.FlexLoader396.instance458.sfiViewer.
    bank.foreground.sa04d14eaa39b3fd.content.clip

    Comments (5)

    I guess, Adobe® AIR™ != Adobe Air®

    adobe air logo
    As my coworker Sergey Kovalyov today had found out by submitting search query to Google with "Adobe Air" keywords that Adobe Air is not only "a cross-operating system runtime", but for more than 65 years, AdobeAir has redefined the standards of quality in evaporative coolers.

    I wonder how copyright claims works in this case....

    Comments (4)

    « Previous entries