The TileLayout is intended to allow developers to get the layout functionality of the Halo Tile container as a Gumbo assignable layout class. That is to layout elements in equally-sized cells in rows and columns.
TileLayout can be used with any Gumbo GroupBase derived container.
Jane is using the new Gumbo classes and she's looking for a Halo-like Tile container but with the functionality of List. She looks through the possible Gumbo layouts and tries to set the List layout as TileLayout. It works!
John is creating a custom skin and he wants to arrange elements in NxN grid equally sized cells. He adds a Group to contain the elements, then sets the layout to be TileLayout. He adjusts the gaps, tweaks the cell sizes. It works.
Here's a high-level list of options that determine the number or columns, number of rows, column width, row height and horizontal gap and vertical gap:
Using one or two options from the above list address a number of scenarios. There are, however, a few scenarios which may require a greater number of settings combinations used:
TileLayout arranges element on screen in rows and columns of equally-sized cells.
The TileLayout performs two major functions - determine measured size of the layout target (container) and layout target's elements in rows and columns of equally-sized cells.
Note the I've updated the prototype to use List and therefore setting only one of explicit width/explicit height will not affect the measured height/width. This is documented in the issues section.
TileLayout calculates a number of properties in updateDisplayList - the number of rows and columns, the size of a single cell, etc. These properties depend on one another and for each the user can specify an explicit override. At the end of updateDisplayList, the TileLayout dispatches events (only if there are any listeners).
Parameters are totalWidth, totalHeight which could be any number or NaN. During updateDisplayList, totalWidth and totalHeight are the unscaledWidth, unscaledHeight parameters, during measure totalWidth and totalHeight are the explicitWidth and explicitHeight overrides of the container.
To allow for crisp graphics and avoid pixel hinting issues the layout needs to take extra care when positioning and sizing elements. In the same time we don't want to add any excessive rounding.
TileLayout will not directly round:
Places where TileLayout will round:
Since elements are positioned and sized at integer coordinates, there are cases where gaps or columns/rows will differ by a pixel. In most cases this is acceptable.
As an illustration here are two extreme cases:
justify column size.
justify horizontal gap.
In order to support pixel scrolling, TileLayout will calculate contentWidth and contentHeight of the container.
Besides that, there are the following LayoutBase scrolling-related APIs. Those get used for keyboard navigation in containers as well as keeping selection (selected item) visible:
public function getHorizontalScrollPositionDelta(unit:ScrollUnit):Number;
public function getVerticalScrollPositionDelta(unit:ScrollUnit):Number;
public function getScrollPositionDelta(index:int):Point;
TileLayout will override getHorizontalScrollPositionDelta and getVerticalScrollPositionDelta
TileLayout will not override getScrollPositionDelta
public class TileLayout extends LayoutBase
{
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// horizontalGap
//----------------------------------
/**
* Horizontal space between columns.
*
* @see #verticalGap
* @see #justifyColumns
* @default 6
*/
public function get horizontalGap():Number
public function set horizontalGap(value:Number):void
//----------------------------------
// verticalGap
//----------------------------------
/**
* Vertical space between rows.
*
* @see #horizontalGap
* @see #justifyRows
* @default 6
*/
public function get verticalGap():Number
public function set verticalGap(value:Number):void
//----------------------------------
// columnCount
//----------------------------------
/**
* Number of columns to be displayed.
* This property will contain the actual column count after
* <code>updateDisplayList()</code>.
* Set to -1 to remove explicit override and allow the TileLayout to determine
* the column count automatically.
*
* Setting this property won't have any effect, if <code>orientation</code> is
* set to "rows", <code>rowCount</code> is explicitly set, and the
* explicit container width is also specified.
*
* @see #rowCount
* @see #justifyColumns
* @default -1
*/
public function get columnCount():int
public function set columnCount(value:int):void
//----------------------------------
// rowCount
//----------------------------------
/**
* Number of rows to be displayed.
* This property will contain the actual rows count after
* <code>updateDisplayList()</code>.
* Set to -1 to remove explicit override and allow the TileLayout to determine
* the row count automatically.
*
* Setting this property won't have any effect, if <code>orientation</code> is
* set to "columns", <code>columnCount</code> is explicitly set, and the
* explicit container height is also specified.
*
* @see #columnCount
* @see #justifyRows
* @default -1
*/
public function get rowCount():int
public function set rowCount(value:int):void
//----------------------------------
// columnWidth
//----------------------------------
/**
* Explicit override for the column width.
* This property will contain the actual column width after
* <code>updateDisplayList()</code>.
*
* <p>If not explicitly set, the column width will be
* determined from the maximum of elements' width.
* Set to NaN to remove explicit override.</p>
*
* If <code>justifyColumns</code> is set to "columnSize", the actual column width
* will grow to justify the fully-visible columns to the container width.
*
* @see #rowHeight
* @see #justifyColumns
* @default NaN
*/
public function get columnWidth():Number
public function set columnWidth(value:Number):void
//----------------------------------
// rowHeight
//----------------------------------
/**
* Explicit override for the row height.
* This property will contain the actual row height after
* <code>updateDisplayList()</code>.
*
* <p>If not explicitly set, the row height will be
* determined from the maximum of elements' height.
* Set to NaN to remove explicit override.</p>
*
* If <code>justifyRows</code> is set to "rowSize", the actual row height
* will grow to justify the fully-visible rows to the container height.
*
* @see #columnWidth
* @see #justifyRows
* @default NaN
*/
public function get rowHeight():Number
public function set rowHeight(value:Number):void
//----------------------------------
// horizontalAlign
//----------------------------------
/**
* Specifies how to align the elements within the cells in the horizontl direction.
* Supported values are
* <code>HorizontalAlign.LEFT</code>,
* <code>HorizontalAlign.CENTER</code>,
* <code>HorizontalAlign.RIGHT</code>,
* <code>HorizontalAlign.JUSTIFY</code>.
*
* @default <code>HorizontalAlign.JUSTIFY</code>
*/
public function get elementHorizontalAlign():String
public function set elementHorizontalAlign(value:String):void
//----------------------------------
// verticalAlign
//----------------------------------
/**
* Specifies how to align the elements within the cells in the vertical direction.
* Supported values are
* <code>VerticalAlign.TOP</code>,
* <code>VerticalAlign.MIDDLE</code>,
* <code>VerticalAlign.BOTTOM</code>,
* <code>VerticalAlign.JUSTIFY</code>.
*
* @default <code>VerticalAlign.JUSTIFY</code>
*/
public function get elementVerticalAlign():String
public function set elementVerticalAlign(value:String):void
//----------------------------------
// justifyColumns
//----------------------------------
<a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%29">Inspectable(category="General")</a>
/**
* Specifies how to justify the fully visible columns to the container width.
* Supported values are "none", "gapSize", "columnSize".
*
* <p>When set to "none" - turns column justificaiton off, there may
* be partially visible columns or whitespace between the last column and
* the right edge of the container. This is the default value.</p>
*
* <p>When set to "gapSize" - the <code>horizontalGap</code> actual value will increase so that
* the last fully visible column right edge aligns with the container's right edge.
* In case there is only a signle fully visible column, the <code>horizontalGap</code> actual value
* will increase so that it pushes any partially visible column just beyond the right edge
* of the container.</p>
*
* <p>When set to "columnSize" - the <code>columnWidth</code> actual value will increase so that
* the last fully visible column right edge aligns with the container's right edge.</p>
*
* @see #horizontalGap
* @see #columnWidth
* @see #justifyRows
* @default "none"
*/
public function get justifyColumns():String
public function set justifyColumns(value:String):void
//----------------------------------
// justifyRows
//----------------------------------
/**
* Specifies how to justify the fully visible rows to the container height.
* Supported values are "none", "gapSize", "rowSize".
*
* <p>When set to "none" - turns column justificaiton off, there may
* be partially visible rows or whitespace between the last row and
* the bottom edge of the container. This is the default value.</p>
*
* <p>When set to "gapSize" - the <code>verticalGap</code> actual value will increase so that
* the last fully visible row bottom edge aligns with the container's bottom edge.
* In case there is only a signle fully visible row, the <code>verticalGap</code> actual value
* will increase so that it pushes any partially visible row just beyond the bottom edge
* of the container.</p>
*
* <p>When set to "rowSize" - the <code>rowHeight</code> actual value will increase so that
* the last fully visible row bottom edge aligns with the container's bottom edge.</p>
*
* @see #verticalGap
* @see #rowHeight
* @see #justifyColumns
* @default "none"
*/
public function get justifyRows():String
public function set justifyRows(value:String):void
//----------------------------------
// orientation
//----------------------------------
/**
* Determines whether to lay out elements in rows or in columns.
* Supported values are "rows", "columns".
*
* @default "rows"
*/
public function get orientation():String
public function set orientation(value:String):void
//--------------------------------------------------------------------------
//
// Overriden methods from LayoutBase
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function measure():void
/**
* @private
*/
override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
}
<Group>
<layout>
<TileLayout columnCount="3" rowCount="4"/>
</layout>
...
<Element1.../>
<Element2.../>
...
</Group>
<Scroller>
<DataGroup id="tileContainer"
itemRenderer="spark.skins.default.DefaultItemRenderer"
dataProvider="{new ArrayList(
'the quick jumped1 fox jumped2 jumped3 lazy jumped4'.split(' '))}">
<layout>
<TileLayout/>
</layout>
</DataGroup>
</Scroller>
Enter implementation/design details for the feature here. This section may be updated after the spec signs off.
Prototype has been implemented to verify feasibility of bindable properties calculation algorithm.
None.
None.
Depends on ILayoutElement exposing APIs to constrain an item size to explicit cell size specified by the user. However explicit cell size should not override explicit minimum / explicit width/height settings.
None.
None.
None.
Describe any accessibility considerations.
None.
No specific globalizaiton issues.
None.
List the RTE message strings that will require localization. (They must all come from .properties files.)
None.
List all UI text that will require localization, such as the month and day names in a DateChooser. (All such text must all come from .properties files.)
None.
List all UI images, skins, sounds, etc. that will require localization.
None.
Discuss any locale-specific formatting requirements for numbers, dates, currency, etc.
None.
Discuss any locale-specific sorting requirements.
None.
When used for a skin part, or a List, the TileLayout can have some seemingly unexpected results:
The problem is that in the List case the TileLayout is in a Group that is constrained inside another Group (the skin). Therefore during measure the TileLayout can see only the internal Group and doesn't have access to the List's explicit width setting (measure executes bottom up).
Suggestion: document issue, don't address in the code. In most cases List is expected to either have both dimensions explicitly specified, or none.
The issue is that there are scenarios where changing TileLayout properties will affect content size calculations in such a way that Scroller will try to show/hide the scrollbars, which will affect the content size again and end up in infinite loop. The issue seems to be that the Scroller is using contentWidth, contentHeight from the last layout pass without taking steps to prevent infinite loops.
This should be addressed in Scroller. There may already be a bug on that.
A question was brought up whether it will be useful to have these convenience classes.
Current recommendation: Don't add anything extra, these are easy enough and if needed we could add them at any point in the future.
Describe any documentation issues or any tips for the doc team.
If there are testing tips for QA, note them here, include a link to the test plan document.