Post-transform bounds - bounds of an object in object's parent coordinate space, i.e. bounts of the transformed object.
Pre-transform bounds - bounds of an object in object's own coordinate space, i.e. bounds of the untransformed object (object's dimensions).
This spec describes an interface that components/objects need to implement in order to be supported by the Gumbo layouts. This is an internal system interface (i.e. doesn't show up in mxml). This interface is purely a contract between the layout and the layout items.
This interface is not intended to be used by app developers/designers to specify user defined layout properties (i.e. properties like width="100%", minWidth="20", etc.). There will be a separate spec describing user defined layout properties for Gumbo.
Implement Gumbo layouts. The layout code will call directly methods on the ILayoutElement interface to arrange elements.
public class VerticalLayout:ILayout
{
public function measure():void
{
var width:Number = 0;
var height:Number = 0;
var count:int = layoutTarget.numLayoutItems;
for (var i:int = 0; i < count; i++)
{
var el:ILayoutElement = layoutTarget.getLayoutElementAt(i);
if (!el|| !el.includeInLayout)
continue;
width = Math.max(width, el.getPreferredBoundsWidth());
height += el.getPreferredBoundsHeight();
}
layoutTarget.measuredWidth = width;
layoutTarget.measuredHeight = height;
}
}
Implement Gumbo components/GraphicElements. Whenever implementing objects that need to be arranged by the Gumbo layouts, those objects need to implement the ILayoutElement interface. In most cases the interface will be implemented in the base classes and developers can choose to override only particular ILayoutElement methods.
public class CustomComponent extends UIComponent implements ILayoutElement
{
public function getPreferredBoundsWidth(postTransform:Boolean=true):Number
{
if (!parentCoordinates)
return getExplicitOrMeasuredWidth;
return TransformUtil.transformBounds(getExplicitOrMeasuredWidth(),
getExplicitOrMeasuredHeight(),
transform.matrix).x;
}
}
Developers can use the ILayoutElement interface to query for size/position as set by the layout system by calling getLayoutPosition and getLayoutSize after the layout pass has completed.
public function updateCompleteListener(event:Event)
{
var el:ILayoutElement = event.target as ILayoutElement;
if (!el)
return;
// Find the layout bounding box:
var top:Number = el.getLayoutBoundsX();
var left:Number = el.getLayoutBoundsY();
var width:Number = el.getLayoutBoundsWidth();
var height:Number = el.getLayoutBoundsHeight();
....
}
The ILayoutElement interface represents an object that supports being laid out. Any object that needs to be included in the Gumbo layout, needs to implement ILayoutElement interface. The layout element itself as a concept is a simple x, y axis-aligned bounding box in parent coordinate space. Object complex transforms, rotations, 3D, etc. can be abstracted away by this interface and are taken care of in the ILayoutElement implementations for the given object type (i.e. the implementations are responsible for converting between pre-transfrom and post-transform coordinates) The interface should also expose low-level information such as pre-transform bounds, transfomration matrix.
In Halo the layout always specifies both width & height of the object. However we need to allow for more general cases, where the width & height of the object bounds may be co-dependent. Examples are:
When a layout determines the size of an object, that size may depend on object's parent dimensions and layout calculations (i.e. both left & right are specified for an object in a canvas layout, or we have percent size in a vertical layout). When an object's width/height, as determined by the layout, has such a dependency we say it's "flexible" since it'll change whenever the size of the parent changes.
In Halo we have a couple of cases for object sizing with several sub-cases
What we propose for the Gumbo ILayoutElement interface API covers the same cases, however it allows ILayoutElement implementations to return different sizes in certain cases. This is new behavior which the new Gumbo layouts need to account for.
The ILayoutElement implementation would:
An example illustrating this is a text label that has width=100% and height unspecified.
<panel title="Pannel" height="300">
<label text="This is a long line of text." width="100%">
</label></panel>
The Gumbo layout would calculate the width in pixels and pass it to the ILayoutElement::setLayoutSize, the implementation will calculate the new height based on the specified width and return the new size.
As a consequence the layout will need to deal with the fact that setLayoutSize() could return bounds size different from the specified. There are a couple of options for a general strategy to handle this new behavior (details will be in a separate spec):
Additionally, to allow for custom layouts that explicitly calculate transforms and sizes in child coordinates, the ILayoutElement APIs must be able to work in both post and pre-transform coordinates as well as provide ways to set the layout transformation matrix explicitly.
/**
* The ILayoutElement interface is used primarily by the layout classes to query,
* size and position the elements of the GroupBase based containers.
*/
public interface ILayoutElement
{
/**
* @copy mx.core.IVisualElement#left
*/
function get left():Object;
/**
* @copy mx.core.IVisualElement#right
*/
function get right():Object;
/**
* @copy mx.core.IVisualElement#top
*/
function get top():Object;
/**
* @copy mx.core.IVisualElement#bottom
*/
function get bottom():Object;
/**
* @copy mx.core.IVisualElement#horizontalCenter
*/
function get horizontalCenter():Object;
/**
* @copy mx.core.IVisualElement#verticalCenter
*/
function get verticalCenter():Object;
/**
* @copy mx.core.IVisualElement#baseline
*/
function get baseline():Object;
/**
* @copy mx.core.IVisualElement#percentWidth
*/
function get percentWidth():Number;
/**
* @copy mx.core.IVisualElement#percentHeight
*/
function get percentHeight():Number;
/**
* Indicates whether the layout should ignore this element or not.
*/
function get includeInLayout():Boolean;
/**
* @return Returns the element's preferred width. Preferred width is
* usually based on the default element size and any explicit overrides.
* For UIComponent this is the same as getExplicitOrMeasuredWidth().
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box width. Bounding box is in element's parent
* coordinate space and is calculated from the element's perferred size and
* layout transform matrix.
*
* @see #getPreferredBoundsHeight
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getPreferredBoundsWidth(postTransform:Boolean = true):Number;
/**
* @return Returns the element's preferred height. Preferred height is
* usually based on the default element size and any explicit overrides.
* For UIComponent this is the same as getExplicitOrMeasuredHeight().
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box height. Bounding box is in element's parent
* coordinate space and is calculated from the element's perferred size and
* layout transform matrix.
*
* @see #getPreferredBoundsWidth
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getPreferredBoundsHeight(postTransform:Boolean = true):Number;
/**
* Returns the element's minimum width.
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box width. Bounding box is in element's parent
* coordinate space and is calculated from the element's minimum size and
* layout transform matrix.
*
* @see #getMinBoundsHeight
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getMinBoundsWidth(postTransform:Boolean = true):Number;
/**
* Returns the element's minimum height.
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box height. Bounding box is in element's parent
* coordinate space and is calculated from the element's minimum size and
* layout transform matrix.
*
* @see #getMinBoundsWidth
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getMinBoundsHeight(postTransform:Boolean = true):Number;
/**
* Returns the element's maximum width.
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box width. Bounding box is in element's parent
* coordinate space and is calculated from the element's maximum size and
* layout transform matrix.
*
* @see #getMaxBoundsHeight
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getMaxBoundsWidth(postTransform:Boolean = true):Number;
/**
* Returns the element's maximum height.
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box height. Bounding box is in element's parent
* coordinate space and is calculated from the element's maximum size and
* layout transform matrix.
*
* @see #getMaxBoundsWidth
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getMaxBoundsHeight(postTransform:Boolean = true):Number;
/**
* Returns the element's layout width. This is the size that the element uses
* to draw on screen.
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box width. Bounding box is in element's parent
* coordinate space and is calculated from the element's layout size and
* layout transform matrix.
*
* @see #getLayoutBoundsHeight
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getLayoutBoundsWidth(postTransform:Boolean = true):Number;
/**
* Returns the element's layout height. This is the size that the element uses
* to draw on screen.
*
* @param postTransform When postTransform is true the method returns
* the element's bounding box width. Bounding box is in element's parent
* coordinate space and is calculated from the element's layout size and
* layout transform matrix.
*
* @see #getLayoutBoundsWidth
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getLayoutBoundsHeight(postTransform:Boolean = true):Number;
/**
* Returns the x coordinate that the element uses to draw on screen.
*
* @param postTransform When postTransform is true the method returns
* x coordinate of the element's bounding box top-left corner.
* Bounding box is in element's parent coordinate space and is calculated
* from the element's layout size, layout position and layout transform matrix.
*
* @see #getLayoutBoundsY
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getLayoutBoundsX(postTransform:Boolean = true):Number;
/**
* Returns the y coordinate that the element uses to draw on screen.
*
* @param postTransform When postTransform is true the method returns
* y coordinate of the element's bounding box top-left corner.
* Bounding box is in element's parent coordinate space and is calculated
* from the element's layout size, layout position and layout transform matrix.
*
* @see #getLayoutBoundsX
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function getLayoutBoundsY(postTransform:Boolean = true):Number;
/**
* Sets the coordinates that the element uses to draw on screen.
*
* @param postTransform When postTransform is true, the element is positioned
* in such a way that the top-left corner of its bounding box is (x, y).
* Bounding box is in element's parent coordinate space and is calculated
* from the element's layout size, layout position and layout transform matrix.
*
* Note that calls to setLayoutBoundsSize can affect the layout position, so
* setLayoutBoundsPosition should be called after setLayoutBoundsSize.
*
* @see #setLayoutBoundsSize
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function setLayoutBoundsPosition(x:Number, y:Number, postTransform:Boolean = true):void;
/**
* Sets the layout size to the specified dimensions. This is the size that
* the element uses to draw on screen.
*
* If one of the dimensions is left unspecified (NaN), it's size
* will be picked such that element can be optimally sized to fit the other
* dimension. This is useful when the caller doesn't want to
* overconstrain the element, for example when the element's width and height
* are corelated (text, components with complex transforms, etc.)
* If both dimensions are left unspecified, the element will have its layout size
* set to its preferred size.
*
* <code>setLayoutBoundsSize</code> does not clip against minium or maximum sizes.
*
* Note that calls to setLayoutBoundsSize can affect the layout position, so
* setLayoutBoundsSize should be called before setLayoutBoundsPosition.
*
* @param width The target width.
*
* @param height The target height.
*
* @param postTransform When postTransform is true, the specified dimensions
* are those of the element's bounding box.
* Bounding box is in element's parent coordinate space and is calculated
* from the element's layout size, layout position and layout transform matrix.
*
* @see #setLayoutBoundsPosition
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function setLayoutBoundsSize(width:Number = NaN,
height:Number = NaN,
postTransform:Boolean = true):void;
/**
* Returns the layout transform Matrix for this element.
* Don't directly modify the return value but call setLayoutMatrix instead.
*/
function getLayoutMatrix():Matrix;
/**
* Sets the transform Matrix that is used to calculate the component's layout
* size and position relative to its siblings.
*
* Note that layout Matrix is factored in the getPreferredSize(),
* getMinSize(), getMaxSize(), getLayoutSize() when computed in parent coordinates
* as well as in getLayoutPosition() in both parent and child coordinates.
*
* <p>The method is typically used by layouts that calculate the transform
* matrix explicitly and work with sizes in child coordinates, ingnoring
* getLayoutPosition. Calling this method does not cause a subsequent layout
* pass (doesn't invalidate element's parent size or display list).</p>
*
* @see #setLayoutMatrix3D
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function setLayoutMatrix(m:Matrix):void;
/**
* Returns the layout transform Matrix3D for this element.
* Don't directly modify the return value but call setLayoutMatrix instead.
*/
function getLayoutMatrix3D():Matrix3D;
/**
* Sets the transform Matrix3D that is used to calculate the component's layout
* size and position relative to its siblings.
*
* Note that layout Matrix3D is factored in the getPreferredSize(),
* getMinSize(), getMaxSize(), getLayoutSize() when computed in parent coordinates
* as well as in getLayoutPosition() in both parent and child coordinates.
*
* <p>The method is typically used by layouts that calculate the transform
* matrix explicitly and work with sizes in child coordinates, ingnoring
* getLayoutPosition. Calling this method does not cause a subsequent layout
* pass (doesn't invalidate element's parent size or display list).</p>
*
* @see #setLayoutMatrix3D
* @see mx.core.UIComponent#layoutMatrix
* @see mx.core.UIComponent#layoutMatrix3D
*/
function setLayoutMatrix3D(m:Matrix3D):void;
}
No compiler changes required by this feature.
No impact by this feature.
No syntax changes for this feature.
Since these layouts will operate only within the new Gumbo components, there should be no behavior/performance implications to existing application.
We've decided not to use ILayoutElement as an wrapper object.
We've decided not to use Point base interface, but have pair of methods returning width and height.