Friday, March 23, 2012

Flex 4: Auto Resize Text Area

In flex 4.0, you can easily create a resizeable text area by setting the property 'heightInLines' with a value of NaN. (I was pretty excited with this property and it really works.) But, when I update my flex version to 4.1 and beyond, this property doesn't really work as per what I had expected and after spending some time debugging and googling, it seems that this property has been ceased. Therefore I have created a temporary solution to a auto resize text area.

Sour codes for the resizeable Text Area Component:
<?xml version="1.0" encoding="utf-8"?>
<s:TextArea xmlns:fx="http://ns.adobe.com/mxml/2009" 
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx"
   width="0" height="0" 
    creationComplete="creationCompleteHandler(event)"
   widthInChars="1" heightInLines="1"
   change="changeHandler(event)"
      paddingBottom="2" paddingLeft="2" 
   paddingRight="2" paddingTop="2"
    borderVisible="false" contentBackgroundAlpha="0"
   selectable="false" editable="false" 
   initialize="initializeHandler(event)"
   verticalScrollPolicy="auto" horizontalScrollPolicy="auto">
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.compose.TextFlowLine;
   import flashx.textLayout.conversion.TextConverter;
   import flashx.textLayout.edit.SelectionManager;
   import flashx.textLayout.elements.TextFlow;
   import flashx.textLayout.elements.TextRange;
   
   import mx.core.IUITextField;
   import mx.core.UIComponent;
   import mx.events.FlexEvent;
   
   import spark.components.RichEditableText;
   import spark.events.TextOperationEvent;
   import spark.utils.TextFlowUtil;
   
   //Variables to store the default width and height
   //of this textfield.
   private var _textWidth:Number = 0;
   private var _textHeight:Number = 0;
   
   private var _autoResizeH:Boolean = false;
   
   //Autoresize the height of the text
   //This property will be used to determine
   //whether u need to resize the height of this
   //textfield.
   public function get autoResizeH():Boolean
   {
    return _autoResizeH;
   }
   
   public function set autoResizeH(value:Boolean):void
   {
    _autoResizeH = value;
    if(_autoResizeH)
    {
     resizeContent();
    }
   }
   
   //Autoresize the width of the text
   //This property is pretty much redundant.
   private var _autoResizeW:Boolean = false;
   
   public function get autoResizeW():Boolean
   {
    return _autoResizeW;
   }
   
   public function set autoResizeW(value:Boolean):void
   {
    _autoResizeW = value;
    if(_autoResizeW)
    {
     resizeContent();
    }
   }
   
   //Autoresize the width and height of the text
   //This property will touch on both width and height
   //of this textfield. Rather than using
   //autoResizeW and autoResizeH, use this instead.
   private var _autoResize:Boolean = false;
   
   public function get autoResize():Boolean
   {
    return _autoResize;
   }
   
   public function set autoResize(value:Boolean):void
   {
    _autoResizeW = value;
    _autoResizeH = value;
    _autoResize = value;
    if(_autoResize)
    {
     resizeContent();
    }
   }
   
   //Override the default text function of a TextArea
   override public function set text(value:String):void
   {
    super.text = value;
    if(_autoResize)
    {
     resizeContent();
    }
   }
   
   //Create a new property for htmlText like what we have
   //in Flex 3.
   private var _htmlText:String = "";
   
   public function set htmlText(value:String):void
   {
    _htmlText = value;
    textFlow = TextConverter.importToFlow(value,
     TextConverter.TEXT_FIELD_HTML_FORMAT);
    if(_autoResize)
    {
     resizeContent();
    }
   }
   
   public function get htmlText():String
   {
    return _htmlText;
   }
   
   //This function will handle all the resizing.
   private function resizeContent():void
   {
    if(textFlow)
    {
     if(_autoResizeH && text != "" 
      && (_textHeight == 0 || isNaN(_textHeight)))
     {
      height = NaN;
      var tempToggle:Boolean = false;
      if(!editable)
      {
       tempToggle = true;
      }
      editable = true;
      textFlow.flowComposer.composeToPosition();              
      textFlow.flowComposer.updateAllControllers();
      if(tempToggle)
      {
       editable = false;
      }
      heightInLines = textFlow.flowComposer.numLines;
      validateNow();
     }
     if(_autoResizeW && text != "" 
      && (_textWidth == 0 || isNaN(_textWidth)))
     {
      width = NaN;
      typicalText = text;
      validateNow();
     }
    }else if(textDisplay){
     if(_autoResizeH && text != "" 
      && (_textHeight == 0 || isNaN(_textHeight)))
     {
      RichEditableText(textDisplay).height = NaN;
      RichEditableText(textDisplay).heightInLines = NaN;
      RichEditableText(textDisplay).validateNow();
      this.validateNow();
     }
     if(_autoResizeW && text != "" 
      && (_textWidth == 0 || isNaN(_textWidth)))
     {
      RichEditableText(textDisplay).width = NaN;
      RichEditableText(textDisplay).widthInChars = text.length;
      RichEditableText(textDisplay).validateNow();
      validateNow();
     }
    }
   }
   
   //On CreationComplete, check if resizing is needed.
   protected function creationCompleteHandler(event:FlexEvent):void
   {
    if(_autoResize)
    {
     resizeContent();
    }
   }
   
   //On Change, check if resizing is needed.
   protected function changeHandler(event:TextOperationEvent):void
   {
    if(_autoResize)
    {
     resizeContent();
    }
   }

   //Store the default width and height of this textfield.
   //Note: we need the default width and height when I'm
   //Resizing the Textfield, or when resizeContent() has
   //been executed.
   protected function initializeHandler(event:FlexEvent):void
   {
    if(width != _textWidth)
    {
     _textWidth = width;
    }
    if(height != _textHeight)
    {
     _textHeight = height;
    }
   }
  ]]>
 </fx:Script>
</s:TextArea>

And here's the source codes for my main application.
<?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/mx"
      xmlns:CommonControls="CommonControls.*"
      viewSourceURL="srcview/index.html">
 <fx:Declarations>
  <!-- Place non-visual elements (e.g., services, value objects) here -->
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   //Dummy normal text
   [bindable]
   private var basicText:String = "Lorem ipsum dolor sit amet, " +
    "consectetur adipiscing elit. Fusce quis risus pellentesque " +
    "ipsum venenatis luctus a quis nulla. Cras rhoncus turpis " +
    "vel ligula iaculis sagittis. Donec eget dolor ac lectus " +
    "pharetra convallis et vitae eros. Pellentesque vestibulum " +
    "nisl ut dolor malesuada vel dictum sem ultricies. Ut " +
    "scelerisque mollis quam eu luctus. Curabitur erat massa, " +
    "accumsan sed adipiscing et, mollis quis lectus. Suspendisse " +
    "nec mauris quis mi varius ultricies at eleifend massa.";
   
   //Dummy HTML encoded text
   [bindable]
   private var specialText:String = "<b>Lorem ipsum dolor sit</b> " +
    "amet, <a target='_blank' href='http://www.google.com'>" +
    "consectetur</a> adipiscing elit. Fusce quis risus " +
    "pellentesque ipsum venenatis luctus a quis nulla. Cras " +
    "rhoncus turpis vel ligula iaculis sagittis. Donec eget " +
    "dolor ac lectus pharetra convallis et vitae eros. " +
    "Pellentesque vestibulum nisl ut dolor malesuada vel dictum " +
    "sem <b><a target='_blank' href='http://www.google.com'>" +
    "ultricies</a></b>. Ut scelerisque mollis quam eu luctus. " +
    "Curabitur erat massa, accumsan sed adipiscing et, mollis " +
    "quis lectus. Suspendisse nec mauris quis mi varius ultricies " +
    "at eleifend massa.";
  ]]>
 </fx:Script>
 <s:BorderContainer width="100%" height="100%">
  <s:layout>
   <s:VerticalLayout verticalAlign="middle" 
         horizontalAlign="center"
         gap="0"/>
  </s:layout>
  <s:BorderContainer width="100%">
   <s:layout>
    <s:HorizontalLayout verticalAlign="top" 
         horizontalAlign="left" 
         paddingLeft="2" 
         paddingRight="2"
         gap="0"/>
   </s:layout>
   <s:Label width="200" height="100%" 
      text="Basic Text with w='100%' h='100%' 
      and autoresize='true':" paddingTop="3"/>
   <CommonControls:CustomTextArea text="{basicText}"
             width="100%" 
             height="100%" 
             autoResize="true"/>
  </s:BorderContainer>
  <s:BorderContainer width="100%">
   <s:layout>
    <s:HorizontalLayout verticalAlign="top" 
         horizontalAlign="left" 
         paddingLeft="2" 
         paddingRight="2"
         gap="0"/>
   </s:layout>
   <s:Label width="200" height="100%" 
      text="Basic Text with w='350' h='100':" 
      paddingTop="3"/>
   <CommonControls:CustomTextArea text="{basicText}"
             width="350" 
             height="100" />
  </s:BorderContainer>
  <s:BorderContainer width="100%">
   <s:layout>
    <s:HorizontalLayout verticalAlign="top" 
         horizontalAlign="left" 
         paddingLeft="2" 
         paddingRight="2"
         gap="0"/>
   </s:layout>
   <s:Label width="200" height="100%" 
      text="Basic Text with w='350'
      and autoresize='true':" paddingTop="3"/>
   <CommonControls:CustomTextArea text="{basicText}"
             width="350" 
             autoResize="true"/>
  </s:BorderContainer>
  <s:BorderContainer width="100%">
   <s:layout>
    <s:HorizontalLayout verticalAlign="top" 
         horizontalAlign="left" 
         paddingLeft="2" 
         paddingRight="2"
         gap="0"/>
   </s:layout>
   <s:Label width="200" height="100%" 
      text="Html Text with w='100%' h='100%' 
      and autoresize='true':" paddingTop="3"/>
   <CommonControls:CustomTextArea htmlText="{specialText}"
             width="100%" 
             height="100%" 
             autoResize="true"/>
  </s:BorderContainer>
  <s:BorderContainer width="100%">
   <s:layout>
    <s:HorizontalLayout verticalAlign="top" 
         horizontalAlign="left" 
         paddingLeft="2" 
         paddingRight="2"
         gap="0"/>
   </s:layout>
   <s:Label width="200" height="100%" 
      text="Html Text with w='350' h='100':" 
      paddingTop="3"/>
   <CommonControls:CustomTextArea htmlText="{specialText}"
             width="350" 
             height="100" />
  </s:BorderContainer>
  <s:BorderContainer width="100%">
   <s:layout>
    <s:HorizontalLayout verticalAlign="top" 
         horizontalAlign="left" 
         paddingLeft="2" 
         paddingRight="2"
         gap="0"/>
   </s:layout>
   <s:Label width="200" height="100%" 
      text="Html Text with w='350'
      and autoresize='true':" paddingTop="3"/>
   <CommonControls:CustomTextArea htmlText="{specialText}"
             width="350" 
             autoResize="true"/>
  </s:BorderContainer>
 </s:BorderContainer>
</s:Application>

* Click here to view the demo of this example.
^ Click here for the source files of this demo.

No comments:

Post a Comment