blob: ef019709da34c8d125b529034d0afaf9657fc933 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package com.adobe.linguistics.spelling.framework.ui
{
import com.adobe.linguistics.utils.Token;
import flash.display.Shape;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.engine.TextLine;
import flashx.textLayout.compose.TextFlowLine;
import flashx.textLayout.edit.SelectionManager;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.tlf_internal;
import spark.components.RichEditableText;
use namespace tlf_internal;
/**
* <p>This class facilitates drawing of squiggly lines below words for RichEditableText class. RichEditableText is a low-level UIComponent for displaying,
* scrolling, selecting, and editing richly-formatted text. This class is used in the skins of the Spark versions of TextInput and TextArea.
* SparkHighlighter could therefore be used for drawing squiggly lines in these Spark components.</p>
*
* @playerversion Flash 10
* @langversion 3.0
*/
public class SparkHighlighter implements IHighlighter
{
private var mTextField:RichEditableText;
private var mHighlighter:Shape;
/*
* offset point:
*/
private var _offsetPoint:Point;
/**
* The constructor for SparkHighlighter.
* @param richEditableText <code>RichEditableText</code> in which to enable highlighting.
* @playerversion Flash 10
* @langversion 3.0
*/
public function SparkHighlighter( richEditableText:RichEditableText )
{
if (richEditableText == null ) throw new Error("illegal argument.");
mTextField = richEditableText;
mHighlighter = null;
this._offsetPoint = new Point(0,0);
}
/**
* Draw squiggly lines below a given token.
* @param token <code>Token</code> information of the word to be highlighted.
* @playerversion Flash 10
* @langversion 3.0
*/
public function drawSquiggleAt(token:Token):void
{
squiggleWord(token);
}
/**
* Clear all squiggly lines in the component.
* @playerversion Flash 10
* @langversion 3.0
*/
public function clearSquiggles():void
{
if (mHighlighter) {
mTextField.removeChild(mHighlighter);
mHighlighter=null;
}
}
/**
* Set offset point information for scrollable controls. This is used by the highlighter to move
* the squiggly lines as the text scrolls inside the control.
* @param op offset information as a <code>Point</code> instance.
* @playerversion Flash 10
* @langversion 3.0
*/
public function set offsetPoint(op:Point):void{
_offsetPoint = op;
}
/**
* Get offset point information for scrollable controls. This is used by the highlighter to move
* the squiggly lines as the text scrolls inside the control.
* @param op offset information as a <code>Point</code> instance.
* @playerversion Flash 10
* @langversion 3.0
*/
public function get offsetPoint():Point{
return _offsetPoint;
}
// TODO: refactor this code to share with halo components, and support words that cross lines
private function squiggleWord(token:Token):void {
var ta:RichEditableText = mTextField;
if (!ta) return;
if (!mHighlighter) {
mHighlighter= new Shape();
mHighlighter.graphics.clear();
mTextField.addChild(mHighlighter);
}
drawSquigglyLineForRange(token.first, token.last);
// Just adjust the left padding, top padding is not an issue
//var pleft:uint = mTextField.getStyle("paddingLeft");
//mHighlighter.x += pleft;
}
// Draw squiggly line
private function drawSquigglyLineForRange(start:Number, end:Number):void
{
// draw squiggly line
var tf:TextFlow = mTextField.textFlow;
var tflFirst:TextFlowLine = tf.flowComposer.findLineAtPosition(start);
var tflLast:TextFlowLine = tf.flowComposer.findLineAtPosition(end);
var tflIndexFirst:int = tf.flowComposer.findLineIndexAtPosition(start);
var tflIndexLast:int = tf.flowComposer.findLineIndexAtPosition(end);
var tflIndex:int = tflIndexFirst;
if (tflIndexFirst == tflIndexLast)
{
// Draw one line
drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart, end - tflFirst.absoluteStart);
}
else
{
// Multiple lines (very long word)
drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart);
tflIndex++;
while (tflIndex != tflIndexLast) {
drawSquigglyLineAtIndex(tflIndex);
tflIndex++;
}
drawSquigglyLineAtIndex(tflIndexLast, 0, end - tflLast.absoluteStart);
}
}
// Draw a squiggly line at specific line for specific index range
private function drawSquigglyLineAtIndex(lineIndex:Number, startIndex:Number=0, endIndex:Number=0x7FFFFFFF):void
{
var tf:TextFlow = mTextField.textFlow;
var tfl:TextFlowLine = tf.flowComposer.getLineAt(lineIndex);
var rectLine:Rectangle = tfl.getBounds();
if (endIndex == 0x7FFFFFFF) {
drawSquigglyLineAtPoint(rectLine.left, rectLine.bottom, rectLine.right - rectLine.left);
}
else {
// Force to have a valid TextLine
var tl:TextLine = tfl.getTextLine(true);
// TODO: atom index and char index is not matching for some chars, use try/catch to avoid crash
try {
var rectFirst:Rectangle = tl.getAtomBounds(startIndex);
var rectLast:Rectangle = tl.getAtomBounds(endIndex);
drawSquigglyLineAtPoint(rectFirst.left + tfl.x, rectLine.bottom, rectLast.right - rectFirst.left);
} catch (err:Error)
{
//TODO: report error
}
}
}
// Draw a squiggly from point x,y with given width, the line is drew in mHighlighter
private function drawSquigglyLineAtPoint(x:Number, y:Number, width:Number):void
{
mHighlighter.graphics.lineStyle(1, 0xfa0707, .65);
mHighlighter.graphics.moveTo(x, y);
var upDirection:Boolean = false;
var offset:uint = 0;
var stepLength:uint = 2;
for ( var i:uint = 1; offset <= width; i++) {
offset = offset + stepLength;
if ( upDirection )
mHighlighter.graphics.lineTo(x+offset,y);
else
mHighlighter.graphics.lineTo(x+offset,y+stepLength);
upDirection = !upDirection;
}
}
private function getValidFirstWordIndex():int{
var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, 0 + mTextField.horizontalScrollPosition, 0 + mTextField.verticalScrollPosition);
return index;
}
private function getValidLastWordIndex():int{
var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, mTextField.width+mTextField.horizontalScrollPosition, mTextField.height+mTextField.verticalScrollPosition);
return index;
}
}
}