blob: 4b9d0d0e3c690345acd3fe132eadb29220706965 [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 org.apache.royale.core
{
import org.apache.royale.core.IBeadLayout;
import org.apache.royale.core.IBorderPaddingMarginValuesImpl;
import org.apache.royale.core.ILayoutChild;
import org.apache.royale.core.ILayoutHost;
import org.apache.royale.core.ILayoutParent;
import org.apache.royale.core.ILayoutView;
import org.apache.royale.core.IStrand;
import org.apache.royale.core.IUIBase;
import org.apache.royale.core.ValuesManager;
import org.apache.royale.core.layout.EdgeData;
import org.apache.royale.core.layout.MarginData;
import org.apache.royale.events.Event;
import org.apache.royale.events.IEventDispatcher;
/**
* This class is the base class for most, if not all, layouts.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
public class LayoutBase implements IBeadLayout
{
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
public function LayoutBase()
{
}
private var sawInitComplete:Boolean;
/**
* The strand/host container is also an ILayoutChild because
* it can have its size dictated by the host's parent which is
* important to know for layout optimization.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
protected var host:ILayoutChild;
/**
* @copy org.apache.royale.core.IBead#strand
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*
* @royaleignorecoercion org.apache.royale.core.ILayoutChild
* @royaleignorecoercion org.apache.royale.events.IEventDispatcher
*/
public function set strand(value:IStrand):void
{
host = value as ILayoutChild;
var sizeChange:Function = handleSizeChange;
(value as IEventDispatcher).addEventListener("widthChanged", sizeChange);
(value as IEventDispatcher).addEventListener("heightChanged", sizeChange);
(value as IEventDispatcher).addEventListener("sizeChanged", sizeChange);
(value as IEventDispatcher).addEventListener("childrenAdded", handleChildrenAdded);
(value as IEventDispatcher).addEventListener("initComplete", handleInitComplete);
(value as IEventDispatcher).addEventListener("layoutNeeded", handleLayoutNeeded);
}
public function listInterests():Array
{
return ["widthChanged","heightChanged","sizeChanged","childrenAdded","initComplete","layoutNeeded"];
}
public function handleNotification(notification:INotification):void{
switch(notification.name)
{
case "widthChanged":
case "heightChanged":
case "sizeChanged":
handleSizeChange();
break;
case "childrenAdded":
handleChildrenAdded();
break;
case "initComplete":
handleInitComplete();
break;
case "layoutNeeded":
handleLayoutNeeded();
break;
}
}
private var lastWidth:Number = -1;
private var lastHeight:Number = -1;
/**
* Changes in size to the host strand are handled (by default) by running the
* layout sequence. Subclasses can override this function and use event.type
* to handle specific changes in dimension.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
protected function handleSizeChange():void
{
if (host.width == lastWidth &&
host.height == lastHeight) return;
performLayout();
lastWidth = host.width;
lastHeight = host.height;
}
/**
* Handles the addition of children to the host's layoutView by listening for
* size changes in the children.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
protected function handleChildrenAdded():void
{
COMPILE::SWF {
if (sawInitComplete)
{
performLayout();
}
else
{
var n:Number = layoutView.numElements;
for(var i:int=0; i < n; i++) {
var child:IEventDispatcher = layoutView.getElementAt(i) as IEventDispatcher;
child.addEventListener("widthChanged", childResizeHandler);
child.addEventListener("heightChanged", childResizeHandler);
child.addEventListener("sizeChanged", childResizeHandler);
}
}
}
COMPILE::JS {
if (sawInitComplete) {
performLayout();
}
}
}
/**
* If changes happen to a layoutView's child, this function will perform the
* layout again.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
* @royaleignorecoercion org.apache.royale.core.ILayoutParent
*/
protected function childResizeHandler(event:Event):void
{
var viewBead:ILayoutHost;
// don't layout in response to child size changes if sized by parent
// or explicitly sized
if (event.type == "widthChanged" &&
!(host.isWidthSizedToContent() || !isNaN(host.explicitWidth)))
{
// but do call this to update scrolling viewports
viewBead = (host as ILayoutParent).getLayoutHost();
viewBead.beforeLayout();
viewBead.afterLayout();
return;
}
// don't layout in response to child size changes if sized by parent
// or explicitly sized
if (event.type == "heightChanged" &&
!(host.isHeightSizedToContent() || !isNaN(host.explicitHeight)))
{
// but do call this to update scrolling viewports
viewBead = (host as ILayoutParent).getLayoutHost();
viewBead.beforeLayout();
viewBead.afterLayout();
return;
}
// don't layout in response to child size changes if sized by parent
// or explicitly sized
if (event.type == "sizeChanged" &&
!(host.isHeightSizedToContent() || !isNaN(host.explicitHeight)) &&
!(host.isWidthSizedToContent() || !isNaN(host.explicitWidth)))
{
// but do call this to update scrolling viewports
viewBead = (host as ILayoutParent).getLayoutHost();
viewBead.beforeLayout();
viewBead.afterLayout();
return;
}
performLayout();
}
/**
* Called whenever "layoutNeeded" event is dispatched against the host strand.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
protected function handleLayoutNeeded():void
{
performLayout();
}
/**
* Handles the final start-up condition by running the layout an initial time.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
protected function handleInitComplete():void
{
sawInitComplete = true;
COMPILE::SWF
{
// Complete the setup if the height is sized to content or has been explicitly set
// and the width is sized to content or has been explicitly set
if ((host.isHeightSizedToContent() || !isNaN(host.explicitHeight)) &&
(host.isWidthSizedToContent() || !isNaN(host.explicitWidth)))
performLayout();
}
COMPILE::JS
{
// always run layout since there are no size change events
performLayout();
}
}
/**
* Returns an object of margins for the given child.
*
* @param child Object The element whose margins are required.
* @param hostWidth Number The usable width dimension of the host.
* @param hostHeight Number The usable height dimension of the host.
*
* @return Object A structure of {top:Number, left:Number, bottom:Number, right:Number}
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
* @royaleignorecoercion org.apache.royale.core.IBorderPaddingMarginValuesImpl
* @royaleignorecoercion org.apache.royale.core.IUIBase
*/
protected function childMargins(child:Object, hostWidth:Number, hostHeight:Number):MarginData
{
var md:MarginData = (ValuesManager.valuesImpl as IBorderPaddingMarginValuesImpl).getMargins(child as IUIBase, hostWidth, hostHeight);
return md;
}
/**
* Returns an object containing the child's positioning values.
*
* @param child Object The element whose positions are required.
*
* @return Object A structure of {top:Number, left:Number, bottom:Number, right:Number}
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
* @royaleignorecoercion org.apache.royale.core.IBorderPaddingMarginValuesImpl
* @royaleignorecoercion org.apache.royale.core.IUIBase
*/
protected function childPositions(child:Object):EdgeData
{
var ed:EdgeData = (ValuesManager.valuesImpl as IBorderPaddingMarginValuesImpl).getPositions(child as IUIBase);
return ed;
}
/**
* Returns the ILayoutView for the host.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*
* @royaleignorecoercion org.apache.royale.core.ILayoutParent
*/
protected function get layoutView():ILayoutView
{
var viewBead:ILayoutHost = (host as ILayoutParent).getLayoutHost();
return viewBead.contentView;
}
private var isLayoutRunning:Boolean = false;
/**
* Performs the layout in three parts: before, layout, after.
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
* @royaleignorecoercion org.apache.royale.core.ILayoutParent
* @royaleignorecoercion org.apache.royale.events.IEventDispatcher
*/
public function performLayout():void
{
// avoid running this layout instance recursively.
if (isLayoutRunning) return;
isLayoutRunning = true;
/* Not all components need measurement
COMPILE::SWF
{
host.measuredHeight = host.height;
host.measuredWidth = host.width;
}
*/
var viewBead:ILayoutHost = (host as ILayoutParent).getLayoutHost();
viewBead.beforeLayout();
if (layout()) {
viewBead.afterLayout();
}
isLayoutRunning = false;
host.dispatchEvent(new Event("layoutComplete"));
/* measurement may not matter for all components
COMPILE::SWF
{
// check sizes to see if layout changed the size or not
// and send an event to re-layout parent of host
if (host.width != host.measuredWidth ||
host.height != host.measuredHeight)
{
isLayoutRunning = true;
host.dispatchEvent(new Event("sizeChanged"));
isLayoutRunning = false;
}
}
*/
}
/**
* @copy org.apache.royale.core.IBeadLayout#layout
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.6
* @productversion Royale 0.8
*/
public function layout():Boolean
{
// override in subclass
return false;
}
}
}