Wednesday, October 27, 2010

Displaying Html text in Labels

Displaying html content inside Flex labels is not as straight forward as it was in Flex 3 by simply setting the htmlText property. Here are the 3 Spark controls used for displaying text:
  • Label - lightweight plain text only
  • RichText - as the name says it provides support for rich text.
    Also allows html content to be imported (anchor tags <a> don't work)
  • RichEditableText - provides the same rich text support as the RichText control.
    Allows editing and anchor html tags work properly
Here is a list of the supported HTML tags: <div>, <p>, <a>, <span>, <img>, <br>.

As noted above, if you want to use anchor <a> tags, you must use a RichEditableText control. In this case you'd most likely want to also set the editable="false" property.
The anchor href property can be set to a relative path like
<a href="index.html">index</a>
Or an absolute one like
<a href="http://google.com" target="_blank">google</a>
Note that you can set the target property to control whether the link is opened in a new window or not.
If you want to listen for when the user clicks on an anchor it is possible, but involves a lot more work. Basically you import the html string into a TextFlow object. Then you iterate through all the child elements until you find the LinkElement (which represents the anchor tag), and then add a FlowElementMouseEvent.CLICK event listener.

Here is some sample code that I use to achieve this:
/**
 * Converts the html string (from the resources) into a TextFlow object
 * using the TextConverter class. Then it iterates through all the 
 * elements in the TextFlow until it finds a LinkElement, and adds a 
 * FlowElementMouseEvent.CLICK event handler to that Link Element.
 */
public static function addLinkClickHandler(html:String, 
        linkClickedHandler:Function):TextFlow {
  var textFlow:TextFlow = TextConverter.importToFlow(html, 
    TextConverter.TEXT_FIELD_HTML_FORMAT);
  var link:LinkElement = findLinkElement(textFlow);
  if (link != null) {
    link.addEventListener(FlowElementMouseEvent.CLICK, 
          linkClickedHandler, false0true);
  else {
    trace("Warning - couldn't find link tag in: " + html);
  }
  return textFlow;
}

/**
 * Finds the first LinkElement recursively and returns it.
 */
private static function findLinkElement(group:FlowGroupElement):LinkElement {
  var childGroups:Array = [];
  // First check all the child elements of the current group,
  // Also save any children that are FlowGroupElement
  for (var i:int = 0; i < group.numChildren; i++) {
    var element:FlowElement = group.getChildAt(i);
    if (element is LinkElement) {
      return (element as LinkElement);
    else if (element is FlowGroupElement) {
      childGroups.push(element);
    }
  }
  // Recursively check the child FlowGroupElements now
  for (i = 0; i < childGroups.length; i++) {
    var childGroup:FlowGroupElement = childGroups[i];
    var link:LinkElement = findLinkElement(childGroup);
    if (link != null) {
      return link;
    }
  }
  return null;
}

A simple control that I've created and use frequently is called HtmlLabel which extends the RichEditableText class to provide simple support for html text:
<?xml version="1.0" encoding="utf-8"?>
<s:RichEditableText xmlns:fx="http://ns.adobe.com/mxml/2009"
  xmlns:s="library://ns.adobe.com/flex/spark"
  xmlns:mx="library://ns.adobe.com/flex/mx"
  focusEnabled="false"
  selectable="false"
  editable="false">

  <fx:Script>
    <![CDATA[
      import flashx.textLayout.conversion.TextConverter;

      override public function set text(value:String):void {
        super.textFlow = TextConverter.importToFlow(value, 
          TextConverter.TEXT_FIELD_HTML_FORMAT);
      }
    ]]>
  </fx:Script>

</s:RichEditableText>

Spell Checking

Here is a short example of how to get the Adobe Squiggly spell checking tool installed. Squiggly can be downloaded here.

Squiggly files:
  • AdobeSpellingConfig.xml - put this file in your project's src folder. It defines the locations of the dictionaries and which language(s) to use.
  • en_US.aff, en_US.dic - these are the dictionary files. You'll need different ones for different languages of course. By default they go in the src/dictionaries/en_US folder.
  • AdobeSpellingEngine.swc - the main engine library - required, goes in project lib folder.
  • AdobeSpellingUI.swc - the ui library for Flex 3/MX components (e.g. <mx:TextArea>)
  • AdobeSpellingUIEx.swc - the ui library for Flex 4/Spark components (e.g. <s:TextArea>)
** Only use one of AdobeSpellingUI.swc or AdobeSpellingUIEx.swc.

To integrate Spell Checking into a Spark TextArea (id="textArea"), all you have to do is add one line:

SpellUI.enableSpelling(textArea, "en_US");

I did notice one problem with using Squiggly with Spark controls. The default Cut/Copy/Paste/Select All context menu items were not available anymore. I found a workaround for this by getting the contextMenu (which gets set on the RichEditableText control, not the TextArea) and setting clipboardMenu = true. See the example below for more details (right click to View Source).

Squiggly is still in prerelease, so a few bugs are expected. There is a forum here for discussions on Squiggly, and to report problems.