blob: 6fb4cae17c43d8763188ccb6f96b5c7caf80ff40 [file] [log] [blame]
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<!-- Demonstrate some example controls. This example does not attempt to create a control for every property in the TextLayoutFramework -->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" label="Text Editor Example" initialize="init()" backgroundColor="#FFFFFF" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Script>
<![CDATA[
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.edit.EditManager;
import flashx.textLayout.edit.IEditManager;
import flashx.textLayout.edit.ElementRange;
import flashx.textLayout.edit.SelectionState;
import flashx.textLayout.elements.Configuration;
import flashx.textLayout.elements.FlowElement;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.elements.ParagraphElement;
import flashx.textLayout.elements.SpanElement;
import flashx.textLayout.events.StatusChangeEvent;
import flashx.textLayout.events.SelectionEvent;
import flashx.textLayout.formats.ITextLayoutFormat;
import flashx.textLayout.formats.TextLayoutFormat;
import flashx.textLayout.formats.TextAlign;
import flashx.textLayout.formats.VerticalAlign;
import flashx.textLayout.formats.BlockProgression;
import flashx.textLayout.formats.Direction;
import flashx.textLayout.operations.ApplyElementIDOperation;
import flashx.textLayout.operations.ApplyElementStyleNameOperation;
import flashx.textLayout.operations.FlowOperation;
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
import flashx.undo.UndoManager;
import flash.display.Sprite;
import flash.system.Capabilities;
// the textFlow being worked on
private var _textFlow:TextFlow = null;
// container to hold the text
private var _container:Sprite = null;
// ///////////////////////////////////////
// data providers for enumerated list boxes
// ///////////////////////////////////////
static private const textAlignData:Array = [
{ label:"Justify", data:TextAlign.JUSTIFY},
{ label:"Left", data:TextAlign.LEFT},
{ label:"Right", data:TextAlign.RIGHT},
{ label:"Center", data:TextAlign.CENTER},
{ label:"End", data:TextAlign.END},
{ label:"Start", data:TextAlign.START}
];
static private const verticalAlignData:Array = [
{ label:"Bottom", data:VerticalAlign.BOTTOM },
{ label:"Justify", data:VerticalAlign.JUSTIFY },
{ label:"Middle", data:VerticalAlign.MIDDLE },
{ label:"Top", data:VerticalAlign.TOP }
];
static private const blockProgressionData:Array = [
{ label:"TopToBottom", data:BlockProgression.TB },
{ label:"RightToleft", data:BlockProgression.RL }
];
static private const directionData:Array = [
{ label:"LeftToRight", data:Direction.LTR },
{ label:"RightToleft", data:Direction.RTL }
];
static private const styleLevelData:Array = [
{ label:"TextFlow", data:TextFlow },
{ label:"Paragraph", data:ParagraphElement},
{ label:"Span", data:SpanElement}
];
/**
* initialization
*/
private function init():void
{
// create a sprite to hold the TextLines
_container = new Sprite();
textArea.rawChildren.addChild(_container);
fontFamily.dataProvider = populateFontFamily();
versionInfo.text = "Vellum: " + flashx.textLayout.TextLayoutVersion.BUILD_NUMBER + (Configuration.tlf_internal::debugCodeEnabled ? " Debug" : " Release")
+ ", Flex: " + mx_internal::VERSION
+ ", Player: " + Capabilities.version;
}
/**
* Create an array of available font families
*/
static private function populateFontFamily():Array
{
// really this returns an array of fonts - would be nice to strip it down to just the families
var fonts:Array = Font.enumerateFonts(true);
var fontfamily:Array = new Array();
fonts.sortOn("fontName", Array.CASEINSENSITIVE);
for(var i:int = 0; i< fonts.length; i++)
{
// trace(fonts[i].fontName);
fontfamily.push({label: fonts[i].fontName, data: fonts[i].fontName});
}
return fontfamily;
}
/** called to set the size of this panel */
public function setSize(w:int,h:int):void
{
this.width = w;
this.height = h;
textArea.width = width;
textArea.height = height > bottomTabs.height ? this.height-bottomTabs.height : 0;
if (_textFlow)
{
_textFlow.flowComposer.getControllerAt(0).setCompositionSize(textArea.width,textArea.height);
_textFlow.flowComposer.updateAllControllers();
}
}
/** called when the bottom tabs finally gets sized. */
private function bottomTabsResize():void
{
setSize(width,height);
}
/** The TextFlow to edit. */
public function get textFlow():TextFlow
{ return _textFlow; }
public function set textFlow(newFlow:TextFlow):void
{
// clear any old flow if present
if (_textFlow)
{
_textFlow.flowComposer = null;
_textFlow = null;
}
_textFlow = newFlow;
if (_textFlow)
{
_textFlow.flowComposer.addController(new ContainerController(_container,textArea.width,textArea.height));
// setup event listeners for selection changed and ILG loaded
_textFlow.addEventListener(SelectionEvent.SELECTION_CHANGE,selectionChangeListener,false,0,true);
_textFlow.addEventListener(StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,graphicStatusChangeEvent,false,0,true);
// make _textFlow editable with undo
_textFlow.interactionManager = new EditManager(new UndoManager());
// initialize with a selection before the first character
_textFlow.interactionManager.selectRange(0,0);
// compose the new textFlow and give it focus
_textFlow.flowComposer.updateAllControllers();
_textFlow.interactionManager.setFocus();
}
}
/** Receives an event any time an ILG with a computed size finishes loading. */
private function graphicStatusChangeEvent(evt:StatusChangeEvent):void
{
// recompose if the evt is from an element in this textFlow
if (_textFlow && evt.element.getTextFlow() == _textFlow)
_textFlow.flowComposer.updateAllControllers();
}
/** Receives an event any time the selection is changed. Update the UI */
private function selectionChangeListener(e:SelectionEvent):void
{
var selectionState:SelectionState = e.selectionState;
var selectedElementRange:ElementRange = ElementRange.createElementRange(selectionState.textFlow, selectionState.absoluteStart, selectionState.absoluteEnd);
// set display according to the values at the beginning of the selection range. For point selection/characterFormat use getCommonCharacterFormat as that tracks pending attributes waiting for the next character
var characterFormat:ITextLayoutFormat = _textFlow.interactionManager.activePosition == _textFlow.interactionManager.anchorPosition ? _textFlow.interactionManager.getCommonCharacterFormat() : selectedElementRange.characterFormat;
var paragraphFormat:ITextLayoutFormat = selectedElementRange.paragraphFormat;
var containerFormat:ITextLayoutFormat = selectedElementRange.containerFormat;
updateComboBox(fontFamily,characterFormat.fontFamily);
fontSize.text = characterFormat.fontSize.toString();
lineHeight.text = characterFormat.lineHeight.toString();
updateComboBox(textAlign,paragraphFormat.textAlign);
textIndent.text = paragraphFormat.textIndent.toString();
columnCount.text = containerFormat.columnCount.toString();
columnGap.text = containerFormat.columnGap.toString();
updateComboBox(verticalAlign,containerFormat.verticalAlign);
updateComboBox(blockProgression,_textFlow.computedFormat.blockProgression);
updateComboBox(directionBox,_textFlow.computedFormat.direction);
updateStyling();
}
/** Helper function to update a comboBox in the UI */
private function updateComboBox(box:ComboBox,val:String):void
{
for (var i:int = 0; i < box.dataProvider.length; i++)
{
if (box.dataProvider[i].data == val)
{
box.selectedIndex = i;
return;
}
}
box.text = val;
}
/**
* These functions are helpers for the various widgets to actually perform the operations on the TextFlow
*/
private function changeFontFamily(newFontFamily:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var cf:TextLayoutFormat = new TextLayoutFormat();
cf.fontFamily = newFontFamily;
IEditManager(_textFlow.interactionManager).applyLeafFormat(cf);
_textFlow.interactionManager.setFocus();
}
}
private function changeFontSize(newFontSize:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var cf:TextLayoutFormat = new TextLayoutFormat();
cf.fontSize = newFontSize;
IEditManager(_textFlow.interactionManager).applyLeafFormat(cf);
_textFlow.interactionManager.setFocus();
}
}
private function changeLeading(newLeading:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var cf:TextLayoutFormat = new TextLayoutFormat();
cf.lineHeight = newLeading;
IEditManager(_textFlow.interactionManager).applyLeafFormat(cf);
_textFlow.interactionManager.setFocus();
}
}
private function changeTextAlign(newAlign:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var pf:TextLayoutFormat = new TextLayoutFormat();
pf.textAlign = newAlign;
IEditManager(_textFlow.interactionManager).applyParagraphFormat(pf);
_textFlow.interactionManager.setFocus();
}
}
private function changeTextIndent(newIndent:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var pf:TextLayoutFormat = new TextLayoutFormat();
pf.textIndent = newIndent;
IEditManager(_textFlow.interactionManager).applyParagraphFormat(pf);
_textFlow.interactionManager.setFocus();
}
}
private function changeColumnCount(newCount:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var cf:TextLayoutFormat = new TextLayoutFormat();
cf.columnCount = newCount;
IEditManager(_textFlow.interactionManager).applyContainerFormat(cf);
_textFlow.interactionManager.setFocus();
}
}
private function changeColumnGap(newGap:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var cf:TextLayoutFormat = new TextLayoutFormat();
cf.columnGap = newGap;
IEditManager(_textFlow.interactionManager).applyContainerFormat(cf);
_textFlow.interactionManager.setFocus();
}
}
private function changeVerticalAlign(newAlign:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var cf:TextLayoutFormat = new TextLayoutFormat();
cf.verticalAlign = newAlign;
IEditManager(_textFlow.interactionManager).applyContainerFormat(cf);
_textFlow.interactionManager.setFocus();
}
}
private function changeBlockProgression(newProgression:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var cf:TextLayoutFormat = new TextLayoutFormat();
cf.blockProgression = newProgression;
IEditManager(_textFlow.interactionManager).applyFormatToElement(_textFlow,cf);
_textFlow.interactionManager.setFocus();
}
}
/** Set direction on the rootElement. This effects both columnDirection and default reading order. */
private function changeDirection(newDirection:String):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var pf:TextLayoutFormat = new TextLayoutFormat();
pf.direction = newDirection;
IEditManager(_textFlow.interactionManager).applyFormatToElement(_textFlow,pf);
_textFlow.interactionManager.setFocus();
}
}
/** Currently selected level to display */
private var currentStyleLevel:Class = TextFlow;
/** Returns the current FlowELement element at the currentStyleLevel */
private function currentStyleElement(absolutePosition:int):FlowElement
{
var e:FlowElement;
switch(currentStyleLevel)
{
case TextFlow:
e = _textFlow;
break;
case ParagraphElement:
e = _textFlow.findLeaf(absolutePosition).getParagraph();
break;
default:
e = _textFlow.findLeaf(absolutePosition);
}
return e;
}
/** Update the displayed styles. */
private function updateStyling():void
{
var e:FlowElement = currentStyleElement(_textFlow.interactionManager.anchorPosition);
styleId.text = e.id;
styleName.text = e.styleName;
}
/** Sets the level for the current style element to view. */
private function changeStyleLevel(newLevel:Class):void
{
currentStyleLevel = newLevel;
updateStyling();
}
/** Function to apply the id or styleName to a range. Its a bit tricker than I'd like it to be. */
private function performStyleOperation(newData:String,operationClass:Class):void
{
if (_textFlow && _textFlow.interactionManager is IEditManager)
{
var op:FlowOperation;
var target:FlowElement;
// if its a point selection change or TextFlow change the current element - else change the range
if (_textFlow.interactionManager.isRangeSelection() && currentStyleLevel != TextFlow)
{
// possibly multiple targets over the range
var targetList:Array = [];
var absStart:int = _textFlow.interactionManager.getSelectionState().absoluteStart;
var absEnd:int = _textFlow.interactionManager.getSelectionState().absoluteEnd;
while (absStart < absEnd)
{
target = currentStyleElement(absStart);
targetList.push(currentStyleElement(absStart));
absStart = target.getAbsoluteStart() + target.textLength;
}
absStart = _textFlow.interactionManager.absoluteStart;
IEditManager(_textFlow.interactionManager).beginCompositeOperation();
var selectionEndDelta:int = 0;
for each (target in targetList)
{
//if we are extending the style name then normalize which is called in doOp will cause the element to
//mergeToPrev. The result is an invalid index. Example:
//
//Initial State:
//<span styleID="a">foo</span><span styleID="b">bar</span><span>moreFooBar</span>
//
//Selection from "oo" of "foo" to "more" and new styleID="b"
//"foo" will be split:
//<span styleID="a">f</span><span styleID="b">oo</span>...
//
//During operation, normalize will merge "bar" into "oo":
//span styleID="a">f</span><span styleID="b">oobar</span>
//
//And most importantly, the element of "bar" is removed from the flow and ready for deletion.
//In this case, skip to the next element.
//
//Watson 2291333. - gak 03.13.09
if(target.parent == null)
{
absStart += target.textLength;
continue;
}
// it using the anchhor position to find the style element. need to give it a range of just the first selected element. otherwise need to iterate over many
var operationEnd:int = absEnd < target.getAbsoluteStart()+target.textLength ? absEnd : target.getAbsoluteStart()+target.textLength;
var textFlowLength:int = _textFlow.textLength;
// parameters passed to operation class constructor
// selectionState - ending selection
// target - element to be modified
// newData - id or styleName
// relativeStart - target relative position to begin the modificaiton
// relativeEnd - target relative position to end the modification
op = new operationClass(_textFlow.interactionManager.getSelectionState(),target,newData,absStart-target.getAbsoluteStart(),operationEnd-target.getAbsoluteStart());
IEditManager(_textFlow.interactionManager).doOperation(op);
// new paragraphs might get created due to splitting so adjust for the additional paragraph terminators that were inserted
if (textFlowLength < _textFlow.textLength)
{
absStart += _textFlow.textLength-textFlowLength;
absEnd += _textFlow.textLength-textFlowLength;
operationEnd += _textFlow.textLength-textFlowLength;
selectionEndDelta += _textFlow.textLength-textFlowLength;
}
absStart = operationEnd;
}
IEditManager(_textFlow.interactionManager).endCompositeOperation();
// finally adjust the selection to include any extra chars added by split paragraphs
if (selectionEndDelta > 0)
{
var anchor:int = _textFlow.interactionManager.anchorPosition;
var active:int = _textFlow.interactionManager.activePosition;
if (anchor < active)
active += selectionEndDelta;
else
anchor += selectionEndDelta;
_textFlow.interactionManager.selectRange(anchor,active);
_textFlow.interactionManager.refreshSelection();
}
}
else // change the element containing the point selection
{
target = currentStyleElement(_textFlow.interactionManager.anchorPosition);
op = new operationClass(_textFlow.interactionManager.getSelectionState(),target,newData);
IEditManager(_textFlow.interactionManager).doOperation(op);
}
_textFlow.interactionManager.setFocus();
}
}
private function changeStyleId(newId:String):void
{
performStyleOperation(newId,ApplyElementIDOperation);
}
private function changeStyleStyleName(newStyleName:String):void
{
performStyleOperation(newStyleName,ApplyElementStyleNameOperation);
}
]]>
</mx:Script>
<!-- <mx:VBox horizontalScrollPolicy="off" verticalScrollPolicy="off" width="100%" height="100%"> -->
<mx:Canvas id="textArea" width="520" height="400"/>
<mx:TabNavigator id="bottomTabs" width="100%" creationPolicy="all" paddingLeft="4" paddingBottom="8" backgroundColor="#D9D9D9" color="#202020" horizontalScrollPolicy="off" verticalScrollPolicy="off" resize="bottomTabsResize()">
<mx:HBox label="Text" backgroundColor="#D9D9D9" width="496" horizontalScrollPolicy="off" verticalScrollPolicy="off" >
<mx:Label text="Font:"/>
<mx:ComboBox id="fontFamily" editable="true" enter="changeFontFamily(fontFamily.text)" close="changeFontFamily(fontFamily.text)" width="200"/>
<mx:Label text="Size:"/>
<mx:TextInput id="fontSize" enter="changeFontSize(fontSize.text)" width="40"/>
<mx:Label text="LineHeight:"/>
<mx:TextInput id="lineHeight" enter="changeLeading(lineHeight.text)" width="40"/>
</mx:HBox>
<mx:HBox label="Para" backgroundColor="#D9D9D9" width="496">
<mx:Label text="Alignment:"/>
<mx:ComboBox id="textAlign" close="changeTextAlign(textAlign.selectedItem.data)" dataProvider="{textAlignData}"/>
<mx:Label text="FirstLineIdent:"/>
<mx:TextInput id="textIndent" enter="changeTextIndent(textIndent.text)" width="40"/>
</mx:HBox>
<mx:HBox label="Container" backgroundColor="#D9D9D9" width="496">
<mx:Label text="Columns:"/>
<mx:TextInput id="columnCount" toolTip="auto or a number" enter="changeColumnCount(columnCount.text)" width="40"/>
<mx:Label text="Gap:"/>
<mx:TextInput id="columnGap" toolTip="a number" enter="changeColumnGap(columnGap.text)" width="40"/>
<mx:Label text="VerticalAlignment:"/>
<mx:ComboBox id="verticalAlign" close="changeVerticalAlign(verticalAlign.selectedItem.data)" dataProvider="{verticalAlignData}"/>
</mx:HBox>
<mx:HBox label="Flow" backgroundColor="#D9D9D9" width="496">
<mx:Label text="Progression:"/>
<mx:ComboBox id="blockProgression" close="changeBlockProgression(blockProgression.selectedItem.data)" dataProvider="{blockProgressionData}"/>
<mx:Label text="Direction:"/>
<mx:ComboBox id="directionBox" close="changeDirection(directionBox.selectedItem.data)" dataProvider="{directionData}"/>
</mx:HBox>
<mx:HBox label="Style" backgroundColor="#D9D9D9" width="496">
<mx:Label text="Level:"/>
<mx:ComboBox id="styleLevel" close="changeStyleLevel(styleLevel.selectedItem.data)" dataProvider="{styleLevelData}" selectedIndex="0"/>
<mx:Label text="id:"/>
<mx:TextInput id="styleId" enter="changeStyleId(styleId.text)" width="40"/>
<mx:Label text="stlyleName:"/>
<mx:TextInput id="styleName" enter="changeStyleStyleName(styleName.text)" width="40"/>
</mx:HBox>
<mx:HBox label="Version" backgroundColor="#D9D9D9" width="496">
<mx:TextInput id="versionInfo" editable="false" width="100%"/>
</mx:HBox>
</mx:TabNavigator>
<!-- </mx:VBox> -->
</mx:VBox>