blob: 3de23db9e7119d85e16f1ec67dea9b4eeb933dbc [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 flashx.textLayout.container
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.geom.Rectangle;
import flashx.textLayout.compose.IFlowComposer;
import flashx.textLayout.compose.LayoutFlowComposer;
import flashx.textLayout.conversion.LayoutConfiguration;
import flashx.textLayout.debug.assert;
import flashx.textLayout.elements.ContainerFormattedElement;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
[ExcludeClass]
/** @private
* A container controller to be used for TextFlows that require extended feature set.
*/
public class LayoutContainerController extends ContainerController implements IFloatController
{
private var inlineChildren:Array;
private var lastInlineChildren:Array = []; // inlineChildren during the last compose pass
private var _wrapList:IWrapManager;
// Composition Results
private var _numWraps:int; // number of wraps in this text frame
/** Constructor - creates a new LayoutContainerController instance.
*
* @param cont The DisplayObjectContainer in which to manage the text lines.
* @param compositionWidth The initial width for composing text in the container.
* @param compositionHeight The initial height for composing text in the container.
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function LayoutContainerController(container:Sprite,compositionWidth:Number=100,compositionHeight:Number=100):void
{
super(container, compositionWidth, compositionHeight);
}
public function get wraps():IWrapManager
{
return _wrapList;
}
public function set wraps(wrapsValue:IWrapManager):void
{
if (!(flowComposer is LayoutFlowComposer))
throw new Error("Layout feature set requires LayoutConfiguration");
_wrapList = wrapsValue;
invalidateContents();
}
public function get numWraps():int
{
return _numWraps;
}
public function set numWraps(value:int):void
{
_numWraps = value;
}
/** @private */
override tlf_internal function setRootElement(value:ContainerFormattedElement):void
{
if (value && wraps != null && !(flowComposer is LayoutFlowComposer))
throw new Error("Layout feature set requires LayoutConfiguration");
super.setRootElement(value);
}
/** Find the container of the position in the flow */
//--------------------------------------------------------------------------
//
// Inlines
//
//--------------------------------------------------------------------------
/** inlines */
private function addInlineChild(child:DisplayObject):void
{
if (!container.contains(child))
{
container.addChildAt(child,container.numChildren>0 ? container.numChildren-1 : 0);
}
}
private function removeInlineChild(child:DisplayObject):void
{
// If the graphic was switched from float to inline ("none"), then it has been reparented to a TextLine,
// which causes it not to be a child of the container anymore. We catch this as an exception that we handle.
try
{
container.removeChild(child);
}
catch(e:Error)
{
if (e.errorID != 2025)
throw e;
}
}
public function recordInlineChild(value:DisplayObject):void
{
if (!inlineChildren)
inlineChildren = [];
else if (inlineChildren.indexOf(value) != -1)
return; // don't read it
inlineChildren.push(value);
}
public function computeInlineArea(value:DisplayObject):Rectangle
{
// HACK
var vbc:TextLayoutBaseContainer = value as TextLayoutBaseContainer;
if (vbc)
{
vbc.validateSize();
return new Rectangle(0, 0, vbc.width, vbc.height);
}
var cont:DisplayObjectContainer = value as DisplayObjectContainer;
// HACK Currently using a few weak typing tricks to play
// nicely with Flex components without compiler dependencies.
// Undoubtedly there's a better way to do this.
if (cont.hasOwnProperty("validateNow"))
cont["validateNow"].call(cont);
// This moves the container from the rawChildren into the parent container for the purpose of layout
// It gets moved back later in updateCompositionShapes/createShapes
// TODO: don't do this as its really expensive and forced a complete redraw
// cont.includeInLayout = false;
if (cont.hasOwnProperty("includeInLayout"))
cont["includeInLayout"] = false;
// TEMP - force add and remove
// if (container.parent != this && container.parent != null)
if (cont.parent)
cont.parent.removeChild(cont);
if (isDamaged())
{
// temporary hack until appropriate interface is determined
if (cont.hasOwnProperty("invalidateSize"))
Function(cont["invalidateSize"]).call(cont);
//cont.invalidateSize();
}
if (cont.hasOwnProperty("validateNow"))
cont["validateNow"].call(cont);
var inlineRect:Rectangle = new Rectangle(0, 0, cont.width, cont.height);
return inlineRect;
}
/** @private */
override tlf_internal function clearCompositionResults():void
{
super.clearCompositionResults();
clearLastInlineChildren();
numWraps = 0;
}
private function clearLastInlineChildren():void
{
if (lastInlineChildren)
{
for (var i:int = 0; i < lastInlineChildren.length; i++)
{
var oldInline:DisplayObject = DisplayObject(lastInlineChildren[i]);
if ((inlineChildren.indexOf(oldInline) < 0))
removeInlineChild(oldInline);
}
}
lastInlineChildren = [];
}
/** @private */
override tlf_internal function updateInlineChildren():void
{
// Remove the inlines from last time that are not part of what we are going to redisplay this time
clearLastInlineChildren();
// synchronize the inline shapes beginning at childIdx
// Add in the new shapes from this time that are not already in the list
for each (var inline:DisplayObject in inlineChildren)
{
addInlineChild(inline);
// inlines can update themselves if necessary here
// We need this because editing a table, e.g., curently invalidates
// through the text flow, rather than through the component hierarchy.
// (Is this the right thing to do?)
if (inline is TextLayoutBaseContainer)
TextLayoutBaseContainer(inline).validateNow();
}
if (inlineChildren)
{
lastInlineChildren = inlineChildren.slice();
inlineChildren.length = 0;
}
}
private function clearInlineSelectionShapes(c:DisplayObjectContainer):void
{
if (c.hasOwnProperty("controller"))
c["controller"].clearAllSelectionShapes();
else
{
for (var idx:int = 0; idx < c.numChildren; idx++)
{
var child:DisplayObjectContainer = c.getChildAt(idx) as DisplayObjectContainer;
if (child)
clearInlineSelectionShapes(child);
}
}
}
CONFIG::debug tlf_internal override function validateLines():void
{
// doesn't work in layout yet
}
}
}