Implementing SpellCheck (Squiggly) with the Text Layout Framework (TLF)

I’ve just posted over in the Text Layout forums how I went about implementing Squiggly with “pure” Text Layout Framework… so that’s not using TLF/FTETextField or the Spark components.This is really just an overview which should give plenty to help yo…

I’ve just posted over in the Text Layout forums how I went about implementing Squiggly with “pure” Text Layout Framework… so that’s not using TLF/FTETextField or the Spark components.

This is really just an overview which should give plenty to help you figure out the steps. I can’t paste the exact code because it’s embedded in a client project, but I do refer to some of the TLF functions throughout that you have to make use of, if anyone can suggest improvements, please drop them in the comments.

Copied from the forum post…

Find TextRanges for misspelled words:

  • Get the first Paragraph using textFlow.getFirstLeaf().getParagraph()
  • Loop through all Paragraphs using para.getNextParagraph()
  • For each, run a Regex match (/bw+b/) on para.getText()
  • Spellcheck each result using Squiggly, and for bad words store a TextRange: TextRange(textFlow, para.getAbsoluteStart()+index, para.getAbsoluteStart()+index+word.length-1); where index is incremented to the position proceeding the end of each word (match or no match).

Spellcheck class:

  • Created a static SpellCheck class which loads language dictionary (downloaded from OpenOffice website) and a UserDictionary (stored as a simple text file)
  • Added methods for checkTextFlow(textFlow:TextFlow):Array which returns an array of “bad” TextRanges, a method for getSuggestions(word:String):Array and methods for checkWord() and addUserWord().

Context menu:

  • Extend ContainerController and override menuSelectHandler()… use an instance of my CustomContainerController when creating the TextFlow:
    textFlow.flowComposer.addController( new CustomContainerController() );
  • Loop through flowComposer.numLines, obtain each TextFlowLine from flowComposer and therefore each TextLine.
  • Determine if textLine.getBounds(container).container(container.mouseX, container.mouseY) to find the line they right-clicked.
  • Get the “raw text” for the line: textLine.textBlock.content.rawText.substr(textLine.textBlockBeginIndex);
  • Find the atom clicked: textLine.getAtomIndexAtPoint(container.stage.mouseX, container.stage.mouseY).
  • Find the starting atom of the word (reverse lookup for word boundary i.e.. ” ” or first char in raw text).
  • Determine the word itself by using regex to find the first word from this starting point (/bw+b/).
  • Add ContextMenuItems for “add to dictionary” and suggested words (for the latter also store start/end atoms in ContextMenuItem.data).
  • When user clicks a suggested word, use (interactionManager as EditManager).selectRange(data.start, data.end); (interactionManager as EditManager).insertText(data.word) where “data” is the data property of the clicked ContextMenuItem.

In my “EditableTextField” class, I call my SpellCheck.checkTextFlow() to get the bad TextRanges and…

  • Loop through the badRanges array.
  • Loop from range.absoluteStart to range.absoluteEnd for each TextRange.
  • Find TextFlowLine for “i” in this loop, and therefore the TextLine: containerController.flowComposer.findLineAtPosition(i); textFlowLine.getTextLine();
  • Find atom bounds using textLine.getAtomBounds(charIndex); where charIndex is: i – textFlowLine.absoluteStart.
    Underline… drawRect( bounds.x + textLine.x, bounds.y + textLine.y + bounds.height – textLine.descent – 1, bounds.width, 3)

I’m sure there is a more elegant way, but this seems to work. I believe I read Adobe are working on Squiggly for pure TLF, if not, I hope this helps somebody get on the right track.