Spark Basic Layout - Functional and Design Specification
Summary and Background
In the Spark architecture, the containers and the layouts are separate objects. With Spark, we are providing generic containers, extensible/custom layouts as well as a few stock layouts with functionality that is parallel to the most-commonly used Halo layout containers.
This spec describes the BasicLayout, which is the Spark counterpart of the Canvas absolute layout, but without the advanced constraints support (no support for constraintRows and constraintColumns).
Goals:
- BasicLayout should provide adequate absolute positioning and constraints support similar to the Halo Canvas container.
- Lightweight enough to be the default GroupBase layout.
- Takes advantage of the ILayoutElement interface to support 2D xforms on the elements
- Reasonable scrolling support.
- Layout calculations place elements on pixel boundaries provided that the input is in integer numbers.
Non-goals:
- Preserve the absolutely exact behavior of the Halo Canvas container.
- BasicLayout does not support virtual layout.
Usage Scenarios
- Use absolute positioning by specifying x, y, width, height.
- Use constraints left, right, top, bottom, horizontalCenter, verticalCenter, baseline.
- Use percentWidth and percentHeight constraints
- Use any combination of the above to arrange elements in skins, renderers; components in applications, etc.
Detailed Description
The BasicLayout extends LayoutBase. It is the default layout of a GroupBase.
Order of Precedence to Determine Element's Size
- Percent width/height - the element's width/height is calculated as percentage of parent's width/height minus any left/right/top/bottom constraints. Element's minimum width, height are respected.
- left & right determine width (element width = parent width - left - right), top & bottom determine height (element height = parent height - top - bottom). Element's minimum width, height are respected.
- preferred element size (explicit or measured width/height).
Order of Precedence to Determine Element's Position
- horizontalCenter, verticalCenter - offsets element's center from the parent's center (element's x = (parent width - element's width) / 2).
- baseline - aligns the element's baselinePosition with the baseline constraint (element's y = baseline - baselinePosition).
- left, top
- right, bottom
- x, y
Order of Precedence to Determine Default Extents for an Element
- Both 'left' and 'right' or 'top' and 'bottom' are set - the extents are big enough to fit the element at its preferredSize.
- horizontalCenter/verticalCenter - the extents are big enough to fit the element as offset from the center of the parent at the parent's default size and element's preferred size (extX = 0, extWidth = horizontalCenter * 2 + preferredWidth);
- baseline - the extents are big enough to fit the element as offset from the baseline (extY = 0, extHeight = element.baseline + element.preferredHeight - element.baselinePosition).
- left/right/top/bottom - the extents are big enough to fit the element plus any of the constraints.
Order of Precedence to Determine Minimum Default Extents for an Element
The minimum default extents are calculated the same way as the default extents. The only difference is that we take the element's minimum size instead of the preferred size in those cases where the element's size is constrained to the parent size (both left & right set or %width set, both top & bottom set or %height set).
Calculating the Default Size - measure()
- measuredWidth, measuredHeight, measuredMinWidth, measuredMinHeight initially set to zero
- For each element that is included in the layout calculate the element default extents and default minimum extents as outlined above.
- Set the measuredWidth, measuredHeight to the Maximum of the bottom-right corners of the default extents. measuredWidth, measuredHeight are at least zero.
- Set the measuredMinWidth, measuredMinHeight to the Maximums of the bottom-right corners of the default minimum extents.
Note that we don't take into account the %width, %height to calculate the default size.
Size and Arrange Elements - updateDisplayList(width, height)
- For each element that is included in the layout:
- Apply the constraints to determine the element's size as described above.
- Using the element's size, apply the constraints to determine the element's position as described above.
- container.contentWidth = Max(childX + childWidth)
- container.contentHeight = Max(childY + childHeight)
Note that because of the way the constraints are defined, there are several meaningful ways to combine them. Let's take a look at a Group with size (100,100):
- left=10, right=20, percentWidth=50 - when sized, the element's width will be 50% of 100 - 10 - 20 = 35 pixels. Element's x will always be 10 as per its left constraint.
- left=10, right=20, percentWidth=50, horizontalCenter=0 - same as above the element will be 35 pixels wide, however it will be positioned in the center of the group. (x = 17.5, 18 after rounding).
BasicLayout doesn't override any of the inherited scrolling APIs from LayoutBase.
The default LayoutBase implementation maps
- ScrollBar steps to single pixel scrolling (ScrollUnit.LEFT scrolls a pixel in the left direction) * ScrollBar pages to the width/height of the scrollRect (ScrollUnit.PAGE_LEFT will scroll left by the width of the scrollRect).
Refer to the LayoutBase spec for details on the scrolling APIs.
Pixel Boundaries and Rounding
The BasicLayout takes care to perform rounding for any computation that may yield fractional numbers, assuming that the inputs are always integer numbers:
- childWidth from width and percentWidth is rounded
- childHeight from height and percentHeight is rounded
- childX from width and horizontalCenter is rounded
- childY from height and verticalCenter is rounded
API Description
The BasicLayout extends LayoutBase. There are no additional public APIs beyond that.
Examples and Usage
Align the text to y=40:
<Group>
<layout>
<BasicLayout/>
</layout>
<Label text="blah" baseline="40"/>
</Group>
Left and Right constraints, we don't need to specify the layout as basic, as this is the default
<Group>
<Label text="blah" left="40" right="10" verticalCenter="0"/>
</Group>
Example from the Spark Panel skin, default Skin layout is basic as well:
<SparkSkin xmlns="http://ns.adobe.com/mxml/2009" alpha.disabled="0.5">
.....
<states>
<State name="normal" />
<State name="disabled" />
</states>
<!-- drop shadow -->
<Rect left="0" top="0" right="0" bottom="0">
<filters>
<DropShadowFilter blurX="20" blurY="20" alpha="0.32" distance="11" angle="90" knockout="true" />
</filters>
<fill>
<SolidColor color="0" />
</fill>
</Rect>
<!-- layer 1: border -->
<Rect left="0" right="0" top="0" bottom="0">
<stroke>
<SolidColorStroke color="0" alpha="0.50" weight="1" />
</stroke>
</Rect>
<!-- layer 2: background fill -->
<Rect id="background" left="1" top="1" right="1" bottom="1">
<fill>
<SolidColor color="0xFFFFFF" id="bgFill" />
</fill>
</Rect>
<!-- layer 3: title bar fill -->
<Rect left="1" right="1" top="1" height="30">
<fill>
<LinearGradient rotation="90">
<GradientEntry color="0xE2E2E2" />
<GradientEntry color="0xD9D9D9" />
</LinearGradient>
</fill>
</Rect>
<!-- layer 4: title bar highlight -->
<Rect left="1" right="1" top="1" height="30">
<stroke>
<LinearGradientStroke rotation="90" weight="1">
<GradientEntry color="0xEAEAEA" />
<GradientEntry color="0xBEBEBE" />
</LinearGradientStroke>
</stroke>
</Rect>
<!-- layer 5: text -->
<TextBox id="titleField"
left="10" right="4" top="2" height="30"
verticalAlign="middle" fontWeight="bold">
</TextBox>
<Group id="contentGroup" left="1" right="1" top="32" bottom="1">
</Group>
</SparkSkin>
Additional Implementation Details
Enter implementation/design details for the feature here. This section may be updated after the spec signs off.
Prototype Work
BasicLayout exists and is in a fairly complete state in the trunk.
Compiler Work
No.
Web Tier Compiler Impact
No.
Flex Feature Dependencies
Dependencies on other Flex features.
Backwards Compatibility
No.
Accessibility
Describe any accessibility considerations.
Possible optimizations
- Don't calculate contentWidth, contnetHeight unless scrolling and clipping is in effect
- If an element is not constrained, the BasicLayout doesn't need to specify its position again.
Globalization
No issues.
Localization
Compiler Features
No command-line params, warnings, errors, etc.
Framework Features
No RTE messages.
No UI text, images, skins, sounds.
Issues and Recommendations
Currently there's an issue with performing accurate measurement in cases where some child properties depend on the child's size. We have two such cases presently:
- Child's bounds top-left position may depend on the child's size (e.g. child is rotated, or child is a path). This leads to incorrect measurement anytime updateDisplayList resizes the child, most notably during init time. This is a more-limited problem affecting only the BasicLayout from all stock Spark layouts.
- Child's baselinePosition depends on the child's size. This problem will affect any layout that relies on baselinePosition during measure if the child is resized in updateDisplayList.
Recommendation:
Documentation
Describe any documentation issues or any tips for the doc team.
QA
If there are testing tips for QA, note them here, include a link to the test plan document.