Class FlexboxLayoutAlgorithm410
Summer 2022: Current/most recommended Layout Algorithm (this is the official algorithm for the 4.0.0 release of FlexBuilder) available in the standard (non-Pro) release.
Implements the CSS3 FlexBox Specification following the suggested algorithm in the specification itself (which is not 100% optimal in performance, but produces the 'most correct' visual results).
Inheritance
Implements
Inherited Members
Namespace: NinjaTools.FlexBuilder.LayoutAlgorithms
Assembly: cs.temp.dll.dll
Syntax
public class FlexboxLayoutAlgorithm410 : ILayoutAlgorithmV4x1, IFlexboxDescribableLayoutAlgorithm
Properties
Methods
- (Nullable<Single>) ClampToContainersInnerMinMaxSizes(FlexItem, ContentLength, Axis)
InnerContentLengthOfContainerIfAlreadyKnown(FlexItem, Nullable<Single>, Axis) - this simpler version is used much later, during Spec 9.4.8.3, when an edge-case needs to "clamp the line’s cross-size to be within the container’s computed min and max cross sizes"
Parameters
FlexItem | containerAsItem | |
ContentLength | boxLength | |
Axis | axis |
Returns
Nullable<Single> |
- (ContentLength) ContentLengthItemInAxis(FlexItem, Axis, CSSAvailableSpace, Boolean, Boolean)
Parameters
FlexItem | item | |
Axis | axis | |
CSSAvailableSpace | containersInnerSize | |
Boolean | scaleToFit | |
Boolean | containerIsShowDebugMessages |
Returns
ContentLength |
- (Vector2) ContentSizeOfContainerInAxis(FlexContainer, Axis, CSSAvailableSpace, Boolean, Boolean)
Parameters
FlexContainer | itemIsContainer | |
Axis | axis | |
CSSAvailableSpace | containersInnerSize | |
Boolean | scaleToFit | |
Boolean | containerIsShowDebugMessages |
Returns
Vector2 |
- (ContentLength) GetItemLengthInAxis(FlexItem, Axis, CSSAvailableSpace, Boolean, Boolean)
NOTE: This is where ALMOST ALL execution time happens: either being forced to recursively relayout child FlexContainer(s) and/or being forced to query the Unity Transform tree and run heuristics to decide what an appropriate ContentSize is for a given GameObject.
In several places the Spec is badly defined, and says essentially 'figure it out yourself, invent a 'size' for a thing, and we're going to leave you guessing what that means in this particular context'. This method tries to handle all those cases - in particular: it will calculate the 'most appropriate' content-size for the supplied item, using the contextual information provided (different callers will setup the 'available-space' parameter differently based on their own conextual clues/requirements)
Parameters
FlexItem | item | |
Axis | axis | |
CSSAvailableSpace | containersInnerSize | |
Boolean | scaleToFit | |
Boolean | containerIsShowDebugMessages |
Returns
ContentLength |
- (Boolean) HasValidSize(FlexContainer)
Current implementation: any size with zero OR positive lengths in both dimensions is 'valid' (even if it is 0-width or 0-height); the only 'invalid' sizes are negative ones.
TODO: upgrade this system to actually store explicit valid/invalid data either on each FlexContainer, or in a parallel data structure.
Parameters
FlexContainer | container |
Returns
Boolean |
- (ContentLength)
InnerContentLengthOfContainerIfAlreadyKnown(FlexItem, Nullable, Axis)
The problem: There is a bug in the official spec, in 9.2.2 it glosses-over a core problem: the authors never calculate the value of "the space available to the flex container" (they only ever calculate the space available WITHIN the container, ie available to the container's children) -- and they naively characterise this as "might result in an infinite value" -- but the reality is: it should rarely result in infinite, and it's up to us as implementers to do something considerably more intelligent and responsible than the spec authors did.
This method provides an extensible/replaceable way of making the decision.
Ideal is: scan upwards through the FlexContainer hierarchy until we EITHER find an ancestor FlexContainer that is 'invalid' in size (forcing us to return 'null', aka infinite), OR we find an ancestor with definite INNER size.
Default is: check only the immediate 'container's parent container', and use the rules for %age in an unknown context, which in CSS are: 1. %age width/max-width/height/min-height etc convert to 'AUTO', and 2. padding converts to '0'.
If there's no parent container, or the parent has invalid size, we return null, forcing the Spec-9.3 code to NEVER WRAP lines (because it interprets that as 'infinite space'.
NOTE: The PRIMARY challenge is when you encounter percentage values of padding: if the initial container has non-percentage padding then this method terminates immediately, as it requires only a simple check on "is container's parent container valid or not?"
Parameters
FlexItem | containerAsItem | |
Nullable<Single> | boxLength | |
Axis | axis |
Returns
ContentLength |
- (Boolean) IsDefiniteSize(ContentLength, out Single)
Ideally you should not be using this, but there is one place where the CSSContentLengthConstrainable has already been converted down, using .definiteValue, and so its cleaner for that code to call this variant on IsDefiniteSize(CSSContentLengthConstrainable, out Single) and have the same readable code than to be doing the null-check there.
Parameters
ContentLength | length | |
Single | definiteValue |
Returns
Boolean |
- (Boolean) IsDefiniteSize(CSSContainerLength, out Single)
- (Boolean) IsDefiniteSize(CSSContentLengthConstrainable, out Single)
This is used in two places where the Spec says "if container's cross-size is definite", but the Spec is wrong: the cross-size is NOT definite (according to CSS: "can be determined without performing layout" - https://www.w3.org/TR/css-sizing-3/#definite) in either place but it might be known in each place because the layout needed to determine it has ALREADY been performed DURING this layout-execution. We treat that case "as if" it were definite - and the browser vendors appear to do the same thing, so we assume it is the Spec that is wrong (perhaps the authors didn't read the CSS-Sizing-3 Spec closely enough, and used 'definite' wrongly - but it's hard to tell since 'definite' is so badly defined even there, and Flexbox authors chose to explictly REDEFINE it in a semi-proprietary fashion too!).
Parameters
CSSContentLengthConstrainable | length | |
Single | definiteValue |
Returns
Boolean |
- (Nullable<Single>) MinMainSizeForItemInAxis(FlexItem, Axis, ContentLength, Boolean)
Used by 9.7.x to limit the shrinking of items during flex-shrink
Specification: (incomprehensibly complex) "Clamp each non-frozen item’s target main size by its used min and max main sizes and floor its content-box size at zero." Web-browsers implementation: (slightly more sane, actually how it's done) "Clamp by its used min if it has one, or else by its used size if it has one, or else by its intrinsic minimum content-size in that dimension"
Parameters
FlexItem | element | |
Axis | axis | |
ContentLength | parentSizeForPercentagesInAxis | |
Boolean | showDebugMessages |
Returns
Nullable<Single> |
- ((T1, T2)<List<FlexLine>, Dictionary<FlexItem, Vector2>>) Phase1CalculateChildSizes(FlexContainer, CSSContainerSize, CSSAvailableSpace)
Main method for calculating FlexItem SIZES
(called by the layout algorithm internally, before it does the calculation of POSITIONS).
NOTE: returns "BORDER_BOX" sizes, not BOX sizes nor CONTENT_BOX sizes, nor MARGIN_BOX sizes
Parameters
FlexContainer | container | |
CSSContainerSize | containersNewSize | |
CSSAvailableSpace | whenSizeUnknown_parentsAvailableSpace |
Returns
(T1, T2)<List<FlexLine>, Dictionary<FlexItem, Vector2>> |
Overrides
- (CSSAvailableSpace) Spec_9_2_2_AvailableSpaceWithinContainer(FlexContainer, CSSContainerSize, CSSAvailableSpace)
Spec: 9.2.2 https://www.w3.org/TR/css-flexbox-1/#algo-available
NB: we MUST diverge from the spec here a little - basic FlexContainers can follow the Spec but by definition our RootFlexContainers (which contain the special-case handling for 'how we integrate with the wider Unity UI ecosystem') must be doing something a little different - they have to take into account UnityUI.
NOTE: returns values that are EITHER constraints OR ContentLengths OR null (null meaning 'unknown content-length; for percentage calcs that need a value, use 0 or auto based on contextual clues', as per CSS Sizing Spec)
Parameters
FlexContainer | container | |
CSSContainerSize | constrainedOrCurrentSize | |
CSSAvailableSpace | whenSizeUnknown_parentsAvailableSpace |
Returns
CSSAvailableSpace |
- (List<FlexLine>)
Spec_9_3_5_CollectIntoFlexLines(FlexContainer, List, Dictionary, ContentLength)
Spec: https://www.w3.org/TR/css-flexbox-1/#algo-line-break
NOTE: requires a special ContentLength that has been pre-converted to "0" for MIN_CONTENT, or "null" for MAX_CONTENT (where most code uses null for BOTH of those)
Parameters
FlexContainer | container | |
List<FlexItem> | childItems | |
Dictionary<FlexItem, Single> | childOuterHypotheticalLengths | |
ContentLength | spaceOrInfiniteOrZero |
Returns
List<FlexLine> |
- (Dictionary<FlexItem, Single>)
Spec_9_3_6_ResolveFlexibleLengths(FlexContainer, List, Dictionary, ContentLength)
Spec: https://www.w3.org/TR/css-flexbox-1/#algo-flex
9.3.6: "Resolve the flexible lengths of all the flex items to find their used main size." NB: this delegates 100% to 9.7 (yes, the numbering is weird in the specification itself!)
NOTE: requires a special ContentLength that has been pre-converted to "0" for MIN_CONTENT, or "null" for MAX_CONTENT (where most code uses null for BOTH of those)
Parameters
FlexContainer | container | |
List<FlexLine> | flexLines | |
Dictionary<FlexItem, Single> | childBoxLengths | |
ContentLength | availableMainLength |
Returns
Dictionary<FlexItem, Single> |
- (Dictionary<FlexItem, Single>)
Spec_9_4_11(FlexContainer, List, Dictionary, ContentLength)
Spec: 9.4.11 https://www.w3.org/TR/css-flexbox-1/#algo-stretch
"Determine the used cross size of each flex item [handling STRETCH etc]"
Parameters
FlexContainer | container | |
List<FlexLine> | flexLines | |
Dictionary<FlexItem, Single> | childHypotheticalCrossLengths | |
ContentLength | containerCrossLengthForPercentages |
Returns
Dictionary<FlexItem, Single> |
- (void)
Spec_9_4_8_1(AlignItems, List, out List)
Spec: 9.4.8.1 https://www.w3.org/TR/css-flexbox-1/#algo-cross-line
------- NOTE: this requires --inline-- axis, which is defined ONLY based on Latin-vs-Japanese/Chinese/etc writing mode --------
Collect all the flex items whose inline-axis is parallel to the main-axis, whose align-self is baseline, and whose cross-axis margins are both non-auto. Find the largest of the distances between each item’s baseline and its hypothetical outer cross-start edge, and the largest of the distances between each item’s baseline and its hypothetical outer cross-end edge, and sum these two values. Among all the items not collected by the previous step, find the largest outer hypothetical cross size. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero. If the flex container is single-line, then clamp the line’s cross-size to be within the container’s computed min and max cross sizes. Note that if CSS 2.1’s definition of min/max-width/height applied more generally, this behavior would fall out automatically."
Parameters
AlignItems | containerAlignItems | |
List<FlexItem> | itemsInLine | |
List<FlexItem> | unprocessedItems |
- (void)
Spec_9_4_8_CalculateFlexLinesCrossSizes(FlexContainer, List, Dictionary, CSSContainerLength, CSSAvailableSpace)
Spec 9.4.8: https://www.w3.org/TR/css-flexbox-1/#algo-cross-line
"Calculate the cross size of each flex line."
The Spec authors create a non-defined concept of "a flexline", and then never state what the "size" is of such a thing - is it a box-size, a content-size, a border-size? - leaving it ambiguous how we should later interpret that "size" during sizing and layout.
By implication, what they failed to write is: "the size of a flexline is an outer-size (ie its a MARGIN_BOX size)", since one of their statements is to calculate it from "the container's inner cross size", and the inner-size of a parent is defined as matching the outer-size of its contents.
Parameters
FlexContainer | container | |
List<FlexLine> | flexLines | |
Dictionary<FlexItem, Single> | childHypotheticalCrossLengths | |
CSSContainerLength | containerCrossLength | |
CSSAvailableSpace | containersAvailableSpace |
- (void)
Spec_9_4_9_ExpandFlexLinesIfStretch(FlexContainer, List, CSSContainerLength, CSSAvailableSpace)
Spec 9.4.9: https://www.w3.org/TR/css-flexbox-1/#algo-line-stretch
As per Spec_9_4_8_CalculateFlexLinesCrossSizes(FlexContainer, List<FlexLine>, Dictionary<FlexItem, Single>, CSSContainerLength, CSSAvailableSpace), we treat the flexlines 'sizes' as 'outer sizes'.
"Handle 'align-content: stretch'"
Spec is wrong here: they say the only thing that needs to be definite is "container ... cross size", but their algorithm ALSO requires that the "container ... inner cross size" is definite (which is NOT GUARANTEED by the constraint the Spec authors wrote down - you can have a %age padding (non-definite) with a pixel width (definite)).
AS A RESULT: if the inner-length can't be directly calculated, we skip this section. TODO: using the already-written IsDefinite() method, consider inserting here specific support for trying to resolve the inner-length (requires recursing up the chain / modifying the definite-length calculation)
Parameters
FlexContainer | container | |
List<FlexLine> | lines | |
CSSContainerLength | containerCrossLengthDefinite | |
CSSAvailableSpace | containersAvailableSpace |
- (Dictionary<FlexItem, Single>)
Spec_9_7_ResolveFlexibleLengths_SingleLine(FlexContainer, FlexLine, Dictionary, Single, ContentLength)
Spec: https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths
NB: omits the final step - "Set each item’s used main size to its target main size" - instead returning the 'target main sizes' and leaving it up to the caller to do the 'set' call.
Parameters
FlexContainer | container | |
FlexLine | flexLine | |
Dictionary<FlexItem, Single> | childBoxLengths | |
Single | leftoverSpace | |
ContentLength | containerInnerMainLength |
Returns
Dictionary<FlexItem, Single> |
- (Single)
Spec_Sizing3_StretchFitSize(FlexItem, Nullable, Axis)
Spec: https://www.w3.org/TR/css-sizing-3/#stretch-fit-size
NOTE: this part of the spec is wrong, and has already been overwritten by future 'unfinished' revisions to the Spec, and the hotlinking in the Spec now is broken (the CSS authors didn't bother to check / fix their links when making updates). We're using (as much as possible) the 'old' version that was originally in the Spec, but the people who write CSS made some terrible decisions here (not least: breaking their own documents during updates!)
Historically: "stretch-fit size: The size a box would take if its outer size filled the available space in the given axis; in other words, the stretch fit into the available space, if that is definite."
Parameters
FlexItem | child | |
Nullable<Single> | availableSpace | |
Axis | axis |
Returns
Single |
Exceptions
Exception |