| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.compose |
| { |
| import flash.display.DisplayObject; |
| import flash.display.DisplayObjectContainer; |
| import flash.display.Graphics; |
| import flash.display.Sprite; |
| import flash.geom.Rectangle; |
| import flash.text.engine.TextBaseline; |
| |
| import flashx.textLayout.container.ContainerController; |
| import flashx.textLayout.container.IFloatController; |
| import flashx.textLayout.elements.ContainerFormattedElement; |
| import flashx.textLayout.elements.FlowElement; |
| import flashx.textLayout.elements.FlowGroupElement; |
| import flashx.textLayout.elements.FlowLeafElement; |
| import flashx.textLayout.elements.InlineGraphicElement; |
| import flashx.textLayout.elements.InlineGraphicElementStatus; |
| import flashx.textLayout.elements.ParagraphElement; |
| import flashx.textLayout.elements.TextFlow; |
| import flashx.textLayout.formats.BlockProgression; |
| import flashx.textLayout.formats.Float; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| /** Keeps track of internal state during composition. |
| * |
| * This is the advanced layout version, used when there are floats, wraps, or columns. |
| */ |
| public class LayoutComposeState extends ComposeState |
| { |
| // a single ComposeState that is checked out and checked in |
| static private var _sharedLayoutComposeState:LayoutComposeState; |
| |
| /** @private */ |
| static tlf_internal function getLayoutComposeState():LayoutComposeState |
| { |
| var rslt:LayoutComposeState = _sharedLayoutComposeState ? _sharedLayoutComposeState : new LayoutComposeState(); |
| _sharedLayoutComposeState = null; |
| return rslt; |
| } |
| |
| /** @private */ |
| static tlf_internal function releaseLayoutComposeState(state:ComposeState):void |
| { |
| if (_sharedLayoutComposeState == null) |
| { |
| _sharedLayoutComposeState = state as LayoutComposeState; |
| if (_sharedLayoutComposeState) |
| _sharedLayoutComposeState.releaseAnyReferences(); |
| } |
| } |
| |
| public function LayoutComposeState() |
| { |
| super(); |
| } |
| /** @private */ |
| protected override function createParcelList():IParcelList |
| { |
| return LayoutParcelList.getLayoutParcelList(); |
| |
| } |
| /** @private */ |
| protected override function releaseParcelList(list:IParcelList):void |
| { |
| LayoutParcelList.releaseLayoutParcelList(list); |
| } |
| protected override function initializeForComposer(composer:IFlowComposer,composeToPosition:int,controllerEndIndex:int):void |
| { |
| // We don't allow composing from the middle of a container because with floats parcels are dynamically created, and |
| // there may have been some created before the _startCompositionPosition, and we don't have a way to recreate the full |
| // parcel state without composing all the text. This is an optimization that should be added later when we fix the floats |
| // code. We can cache off information about each parcel that was created, and the text position it was created at, and |
| // use that to recreate the parcel list state. |
| super.initializeForComposer(composer,composeToPosition,controllerEndIndex); |
| _startComposePosition = Math.min(_startComposePosition, _startController.absoluteStart); |
| } |
| |
| // Debugging code to show parcel edges - requires removing graphics.clear from ContainerControllerBase |
| /* CONFIG::debug override public function composeTextFlow(textFlow:TextFlow):int |
| { |
| var value:int = super.composeTextFlow(textFlow); |
| var controller:ContainerController = textFlow.flowComposer.getControllerAt(0); |
| if (controller) |
| { |
| var container:Sprite = textFlow.flowComposer.getControllerAt(0).container as Sprite; |
| var graphics:Graphics = container.graphics; |
| graphics.clear(); |
| graphics.lineStyle(1, 0xFF0000); |
| var boundsArray:Array = LayoutParcelList(_parcelList).getBounds(); |
| for (var i:int = 0; i < boundsArray.length; ++i) |
| { |
| var bounds:Rectangle = boundsArray[i] as Rectangle; |
| graphics.moveTo(bounds.x, bounds.y); |
| graphics.lineTo(bounds.right, bounds.y); |
| graphics.lineTo(bounds.right, bounds.bottom); |
| graphics.lineTo(bounds.left, bounds.bottom); |
| graphics.lineTo(bounds.left, bounds.top); |
| } |
| } |
| |
| return value; |
| } */ |
| |
| /* @private */ |
| override protected function parcelHasChanged(newParcel:Parcel):void |
| { |
| if (_curParcel && newParcel && _curParcel.controller == newParcel.controller && _curParcel.column == newParcel.column) |
| vjDisableThisParcel = true; |
| super.parcelHasChanged(newParcel); |
| } |
| |
| /* @private */ |
| protected override function composeFloat(elem:ContainerFormattedElement,composeFrame:ContainerController):void |
| { |
| elem.createGeometry(null); |
| // TODO: support arbitrary containers? |
| var floatContainer:DisplayObjectContainer = ContainerFormattedElement(elem).flowComposer.getControllerAt(0).container as DisplayObjectContainer; |
| // var floatContainer:DisplayObjectContainer = ContainerFormattedElement(elem).container as DisplayObjectContainer; |
| if (floatContainer != null) // is inline |
| { |
| if (!(composeFrame is IFloatController)) |
| throw("need a float layout-capable controller!"); |
| // Add as an inline, figure out the size of the inline, wrap around the inline |
| var parcelRect:Rectangle = IFloatController(composeFrame).computeInlineArea(floatContainer); |
| |
| // See if it fits. If so, update the state to show the change. If not, remove the item. |
| if (parcelList.createParcel(parcelRect, elem.computedFormat.blockProgression, true /* next text goes below */)) |
| { |
| floatContainer.x = parcelList.left; |
| floatContainer.y = parcelList.top; |
| floatContainer.height = parcelRect.height; |
| IFloatController(parcelList.controller).recordInlineChild(floatContainer); |
| |
| //_contentAlignmentWidth = Math.max(_contentAlignmentWidth, parcelList.right); |
| parcelList.addTotalDepth(parcelRect.height); |
| parcelList.next(); // advance to next parcel |
| } |
| } |
| |
| _curLineIndex = composeFrame.rootElement.getTextFlow().flowComposer.findLineIndexAtPosition(elem.getAbsoluteStart() + elem.textLength); |
| vjDisableThisParcel = true; |
| vjBeginLineIndex = _curLineIndex; |
| } |
| |
| /* |
| * Compose a floating graphic |
| * |
| * @param elem float we'e composing |
| */ |
| protected function composeFloatInline(elem:InlineGraphicElement):void |
| { |
| if (elem.elementHeight == 0 || elem.elementWidth == 0) // can't compose yet -- graphic isn't ready |
| return; |
| |
| var containerElement:ContainerFormattedElement = elem.getAncestorWithContainer(); |
| var blockProgression:String = containerElement.computedFormat.blockProgression; |
| |
| // HACK!!! |
| // if the baselineZero is set to ideographicTop, then the descent is the point size (measured from ideographic top) |
| // but in this case we've already factored that into the line height, so we're adding twice. Very confusing. |
| var effectiveLastLineDescent:Number = 0; |
| if (!isNaN(_lastLineDescent)) |
| effectiveLastLineDescent = _lastLineDescent; |
| |
| var floatRect:Rectangle = new Rectangle(parcelList.left, parcelList.top, elem.elementWidth, elem.elementHeight); |
| if (blockProgression == BlockProgression.RL) |
| { |
| floatRect.left -= effectiveLastLineDescent; |
| if (elem.float == Float.RIGHT) |
| floatRect.offset(0, (parcelList.bottom - elem.elementHeight) - floatRect.y); |
| } |
| else |
| { |
| floatRect.bottom += effectiveLastLineDescent; |
| if (elem.float == Float.RIGHT) |
| floatRect.offset((parcelList.right - elem.elementWidth) - floatRect.x, 0); |
| } |
| |
| // See if it fits. If so, update the state to show the change. If not, remove the item. |
| if (parcelList.createParcelExperimental(floatRect, elem.float == Float.RIGHT ? "left" : "right")) |
| { |
| var graphic:DisplayObject = elem.graphic; |
| if (graphic) |
| { |
| IFloatController(parcelList.controller).recordInlineChild(graphic); |
| graphic.x = parcelList.left; |
| graphic.y = parcelList.top; |
| if (blockProgression == BlockProgression.TB) |
| graphic.y += effectiveLastLineDescent; |
| } |
| |
| if (blockProgression == BlockProgression.TB) |
| { |
| parcelList.addTotalDepth(floatRect.height); |
| //_contentAlignmentWidth = Math.max(_contentAlignmentWidth, floatRect.width); |
| |
| } |
| else |
| { |
| parcelList.addTotalDepth(floatRect.width); |
| //_contentAlignmentWidth = Math.max(_contentAlignmentWidth, floatRect.height); |
| } |
| parcelList.next(); // advance to next parcel |
| } |
| |
| // _curLineIndex = _curParcel.controller.rootElement.getTextFlow().getAbsoluteLineIndex(elem.getAbsoluteStart() + elem.textLength); |
| vjDisableThisParcel = true; |
| // vjBeginLineIndex = _curLineIndex; |
| } |
| |
| // Called from composeParagraphElement when we are starting to compose a line. Has hooks to handle floats. |
| protected override function startLine():void |
| { |
| // Compose floats that appear at the start of the line, before processing any text. That way the following text will |
| // wrap correctly |
| if (_curElement is InlineGraphicElement && InlineGraphicElement(_curElement).float != Float.NONE) |
| { |
| var graphic:InlineGraphicElement = InlineGraphicElement(_curElement); |
| while (graphic && graphic.float != Float.NONE) |
| { |
| composeFloatInline(graphic); |
| graphic = graphic.getNextLeaf() as InlineGraphicElement; |
| } |
| } |
| } |
| |
| // Called from composeParagraphElement when we are starting to compose a line. Has hooks to handle floats. |
| protected override function endLine():void |
| { |
| var currentOffset:int = _curElementOffset; |
| var currentElementStart:int = _curElementStart; |
| var element:FlowLeafElement = _curElement; |
| |
| // advance to the next element, using the rootElement of the container as a limitNode |
| // to prevent going past the content bound to this container |
| if (currentOffset >= element.textLength) |
| { |
| // We may have composed ahead over several spans; skip until we match up |
| // Loop until we use catch up to where the line we just composed ended (pos). |
| // Stop if we run out of elements. Skip empty inline elements, and skip floats |
| // that came at the start of the line before any text -- they've already been |
| // processed. |
| var firstTextSpan:Boolean = true; |
| do{ |
| if (element is InlineGraphicElement && InlineGraphicElement(element).float != Float.NONE) |
| { |
| if (!firstTextSpan) |
| composeFloatInline(element as InlineGraphicElement); |
| } |
| else firstTextSpan = false; |
| currentOffset -= element.textLength; |
| currentElementStart += _curElement.textLength; |
| if (currentElementStart == _curParaStart+_curParaElement.textLength) |
| { |
| break; // reached end of paragraph |
| } |
| element = element.getNextLeaf(); |
| } while (element != null && (currentOffset >= element.textLength || element.textLength == 0 )); |
| } |
| } |
| |
| } |
| } |