Class FlexItem
Flexbox implementation as per CSS3 (https://www.w3.org/TR/css-flexbox-1/#box-model): "... children of a flex container are called flex items and are laid out using the flex layout model."
Each UI element in your UI requires a FlexItem for it to be layed out; Flexbox does not understand UnityUI, it only understands FlexItems and FlexContainers.
Note: A FlexItem has no effect unless its parent object is a FlexContainer or RootFlexContainer - the FlexContainers perform all the actual layout.
When assigning FlexItems to UI elements you have a choice: you can either attach a FlexItem directly onto the UI element (Button, Label, etc), or you can create an empty GameObject, attach the FlexItem to that object, and then add your UI element as a child of this empty GameObject - FlexItem is designed to intelligently detect both situations and adapt accordingly.
Typically you layout UI items in a FlexContainer with these steps:
- Choose a FlexContainer you've already configured
- Add all the child UI elements you require (eg Buttons, Labels, TextFields, etc)
- For each child UI element: attach a FlexItem to that child, and configure it
Creating from script
Recommended to use CreateFlexComponents which simplifies the creation/adding of new instances
Sizing
FlexItem has a large amount of intelligent / heuristic code for auto-sizing and auto-detecting the content of your UI. In Unity, FlexBuilder uses all the information it can to establish the correct size 'by default', and in most cases you don't need to hand-configure the FlexItem settings. The default Flexbox size is 'AUTO', which generally resolves as 'CONTENT' (although see cssWidth and cssHeight), but you can override this at any time by setting the FlexItem's flexBasis to e.g 'LENGTH' and setting a fixed size in pixels.
When the size has been set to 'CONTENT', your current LayoutAlgorithm will use its own internal metrics to decide what the "natural content size" is. Generally this will use classes from NinjaTools.FlexBuilder.ContentSizing.
Positioning
All positioning of FlexItems is handled by their parent FlexContainer.
Reacting to content-changes / Auto re-sizing
Since version 4.0, FlexBuilder is able to auto-detect all changes - both in Editor and in Player - to Flexbox settings that invalidate the layout, and intelligently triggers a relayout immediately.
However: there are situations that only Unity can detect, and they don't provide a mechanism for non-Unity applications or features to respond to them. The most common example is: changing the content of a Text component.
In those cases you may need to manually inform FlexBuilder that Unity has done something that invalidates the auto-calculated content size. This is not necessarily needed: if your FlexItem had a fixed cssWidth AND a fixed cssHeight, then changing the content will have no effect (FlexBuilder wasn't using the content's size to size the FlexItem).
If it is needed, use the method ContentSizePossiblyChanged() to inform FlexBuilder.
CSS3 Settings
The primary settings from the CSS3/Flexbox specification that control size and position of a FlexItem are:
Implements
Inherited Members
Namespace: NinjaTools.FlexBuilder
Assembly: cs.temp.dll.dll
Syntax
public class FlexItem : BFlexComponent, IFlexComponent, IUpgradeableComponent, ILayoutElement
Fields
Properties
- (AlignSelf) alignSelf
Specification: https://www.w3.org/TR/css-flexbox-1/#align-items-property
Effectively overrides the parent FlexContainer's value of alignItems for this FlexItem only.
- (BoxSizing) boxSizing
CSS3's sizing mode for BoxModel, either BORDER_BOX (recommended, should be the default, used in all modern CSS), or CONTENT_BOX (to be avoided, only exists in CSS for backwards compatibility). Changing this setting changes the meaning/interpretation of cssWidth, cssHeight, and all related properties.
- (LengthOrAuto) cssHeight
The 'height' setting in CSS3, which is only used by Flexbox if the flexBasis hasn't provided a height (i.e. only if basis is set to 'AUTO' or the parent FlexContainer has a direction of 'ROW', which would mean the basis is being ignored for height anyway)
cssMinHeight cssMaxHeight- (Margins) cssMargins
Specification: https://www.w3.org/TR/css-box-3/#margins
Margins add space AROUND this item, forcing other sibling items to be pushed away.
- (LengthOrNone) cssMaxHeight
'max-height' from CSS3, applied to the height and/or the flexBasis, whichever one is generating the height
- (LengthOrNone) cssMaxWidth
'max-width' from CSS3, applied to the width and/or the flexBasis, whichever one is generating the width
- (Length) cssMinHeight
'min-height' from CSS3, applied to the height and/or the flexBasis, whichever one is generating the height
- (Length) cssMinWidth
'min-width' from CSS3, applied to the width and/or the flexBasis, whichever one is generating the width
- (Padding) cssPadding
Specification: https://www.w3.org/TR/css-box-3/#paddings
Padding adds space INSIDE this item, forcing its children to pushed away from the inner walls.
NOTE: Padding is only effective for FlexContainers, since Unity does not understand or respect this concept (Unity's built-in rendering model is weaker and less well-designed than CSS, it has no concept of margins/padding). With FlexContainers, any Padding on their attached FlexItem will be used to internally pad the FlexContainer and push the child FlexItems away from the walls.
This behaviour is frequently exploited to enable you to rapidly add a background texture to a UI Element while also pushing children away from the edge: if you take a FlexContainer and add a child GameObject with an Image/RawImage element but no FlexItem, UnityUI will size that image to cover the whole of the parent FlexContainer. But if you also set padding values on the FlexContainer's FlexItem, then its child FlexItems will be pushed away from the edges.
- (Length) cssPaddingBottom
Allows you to directly set cssPadding.left|right|top|bottom without having to: copy-out the struct, set the value, write the struct back, and then manually invoke the OnFlexPropertyPathModified callback with unique arg.
- (Length) cssPaddingLeft
Allows you to directly set cssPadding.left|right|top|bottom without having to: copy-out the struct, set the value, write the struct back, and then manually invoke the OnFlexPropertyPathModified callback with unique arg.
- (Length) cssPaddingRight
Allows you to directly set cssPadding.left|right|top|bottom without having to: copy-out the struct, set the value, write the struct back, and then manually invoke the OnFlexPropertyPathModified callback with unique arg.
- (Length) cssPaddingTop
Allows you to directly set cssPadding.left|right|top|bottom without having to: copy-out the struct, set the value, write the struct back, and then manually invoke the OnFlexPropertyPathModified callback with unique arg.
- (LengthOrAuto) cssWidth
The 'width' setting in CSS3, which is only used by Flexbox if the flexBasis hasn't provided a width (i.e. only if basis is set to 'AUTO' or the parent FlexContainer has a direction of 'COLUMN', which would mean the basis is being ignored for width anyway)
cssMinWidth cssMaxWidth- (FlexboxBasis) flexBasis
Primary 'size' setting in Flexbox: Specification https://www.w3.org/TR/css-flexbox-1/#flex-basis-property
- (Single) flexGrow
Specification: https://www.w3.org/TR/css-flexbox-1/#flex-grow-property
Flexbox's 'grow' factor: set this to 0 to prevent the item from ever growing to consume available space, or to any positive number to have it share the spare space with sibling flexitems proportional to their own flexGrow values.
- (Single) flexibleHeight
UnityUI built-in property that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (Single) flexibleWidth
UnityUI built-in property that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (Int32) flexOrder
Specification: https://www.w3.org/TR/css-flexbox-1/#flex-shrink-property
"Items with the same ordinal group are laid out in the order they appear in the source document"
Where multiple FlexItems have the same value for flexOrder, FlexBuilder uses their Transform order (ie. which one appears first in the list in Unity's Hierarchy panel) to decide where to lay them out.
- (Single) flexShrink
Specification: https://www.w3.org/TR/css-flexbox-1/#flex-shrink-property
Flexbox's 'shrink' factor: set this to 0 to prevent the item from ever shrinking below its default/initial/configured size, or to any positive number to have it share the shrinking with sibling flexitems proportional to their own flexShrink values.
- (Single) forcePadButtonWidth
Extends the workaround for Unity's Text bugs ... to also use it to fix the bugs in Unity's Button class (which often needs to be 'padded' around the Text to make it fit within the visual bounds of the background image - but Unity failed to include this incredibly basic core feature, and refused to ever fix it)
- (Single) forcePadTextHeight
THIS VARIABLE IS ONLY USED IF THE FLEXITEM HAS AN ATTACHED OR FIRST-CHILD COMPONENT OF TYPE 'Text' - it is a workaround to Unity's bugs in UI.Text
Exists to workaround annoying bugs in Core Unity that Unity refuses to fix (they know the bugs exist, they know they were supposed to fix them 5, 10 years ago, but the people who wrote the code have moved to different teams and no-one now wants to take responsibility for fixing what's broken - as of 2022 Unity QA refuses to allow developers to work on these severe bugs).
Unity lies about the size of text, specifically the line-height. There is no public API for finding the true line-height, and Unity mis-calculates it in a number of places. Even with the Winter 2021 fixes (10 years late!) to some of the core libraries, Unity STILL gets this wrong.
Worse: UnityUI artificially DELETES ANY TEXT that has a render-height less than the (incorrect, internal, private) estimated line-height. To avoid this, we have to (sometimes) artificially tell Unity the text is bigger than it really is. I hate that this code exists: all Unity had to do was fix any ONE of their bugs, or to think sanely about the design of their API in the first place, or take any responsibility for shipping working code
- but they did none of those things.
- (Int32) layoutPriority
UnityUI built-in property that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (Single) minHeight
UnityUI built-in property that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (Single) minWidth
UnityUI built-in property that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (HierarchyObjectType) parentObjectType
- (Single) preferredHeight
UnityUI built-in property that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (Single) preferredWidth
UnityUI built-in property that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (Version) versionCreatedBy
Overrides
- (Version) versionUpgradedTo
Overrides
Methods
- (void) Awake()
- (FlexItem.BoxSizes) CalculateBoxSizesIfResolved()
If this FlexItem has already been rendered, and has not changed since the last render, this method calculates and returns the exact CSS boxes: content-box, border-box, padding-box, and margin-box. i.e. this is INVALID during a layout/relayout operation (because those values haven't be resolved yet), and INVALID for newly-created items that haven't participated in a relayout yet.
It is mostly useful for rendering custom inspectors, and for debugging OUTSIDE of layout/relayout operations.
NOTE: for setting a RectTransform.size, FlexBuilder ALWAYS uses the ContentBox size (this is an intrinsict requirement of integrating with Unity's rendering system, indpendent of the BoxMode of the FlexItem)
Returns
FlexItem.BoxSizes |
Exceptions
Exception |
- (FlexItem.BoxSizes) CalculateBoxSizesIfResolved(out Single, out Single, out Single, out Single, out Single, out Single)
As for CalculateBoxSizesIfResolved() but provides some of the internally calculated temp vars that you may need when doing things with the boxes.
Parameters
Single | borderBoxTop | Distance between top edge of borderbox and its containing box (marginbox) |
Single | borderBoxLeft | Distance between left edge of borderbox and its containing box (marginbox) |
Single | paddingBoxTop | ALWAYS ZERO SINCE BORDERS NOT YET SUPPORTED: Distance between top edge of paddingbox and its containing box (borderbox) |
Single | paddingBoxLeft | ALWAYS ZERO SINCE BORDERS NOT YET SUPPORTED: Distance between left edge of paddingbox and its containing box (borderbox) |
Single | contentBoxTop | Distance between top edge of contentbox and its containing box (paddingbox) |
Single | contentBoxLeft | Distance between left edge of contentbox and its containing box (paddingbox) |
Returns
FlexItem.BoxSizes |
Exceptions
Exception |
- (Single) CalculateInnerLengthForLength(Single, RectTransform.Axis)
Inner length for an item is: its own length, minus any padding (if applicable, based on box-mode), with percentage-paddings calculated relative to its own length
Parameters
Single | length | The item's own length |
RectTransform.Axis | axis |
Returns
Single |
Exceptions
Exception |
- (void) CalculateLayoutInputHorizontal()
UnityUI built-in method that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (void) CalculateLayoutInputVertical()
UnityUI built-in method that is completely disabled by Flexbox, in favour of the faster and more accurate Flexbox algorithm
- (void) ContentSizePossiblyChanged()
This SHOULD be called whenever you think the content size MIGHT have changed - e.g. if this has a Text component and the user changed the .text string, that implies that the size might have changed (if you can verify that it has NOT changed - e.g. because the size was independent of the text, was definite/fixed to start with - then you do NOT need to call this method)
TODO: remove this method or make it more intelligent (able to cache/access cached values, and compare to current/new content-size)
Exceptions
Exception |
- (BoxLength) cssClampLength(BoxLength, RectTransform.Axis, CSSAvailableSpace2)
Convenience method for Flex: clamp a length to min/max, automatically using width or height
Parameters
BoxLength | length | |
RectTransform.Axis | axis | |
CSSAvailableSpace2 | containerSize |
Returns
BoxLength |
- (Single) cssClampLength(Single, RectTransform.Axis, CSSAvailableSpace)
Convenience method for Flex: clamp a length to min/max, automatically using width or height
Parameters
Single | length | |
RectTransform.Axis | axis | |
CSSAvailableSpace | containerSize |
Returns
Single |
- (Single) cssClampLengthToInnerSizes(ContentLength, RectTransform.Axis, CSSContentLengthConstrainable)
Special: the supplied length is a ContentLength, this method clamps against the INNER bounds of the item's MIN and MAX (instead of clamping against them as pure floats)
Parameters
ContentLength | innerLength | |
RectTransform.Axis | axis | |
CSSContentLengthConstrainable | containerInnerLength |
Returns
Single |
- (LengthOrAuto) cssLength(RectTransform.Axis)
Convenience method for Flex: cssWidth OR cssHeight - Flex can switch axis arbitrarily
Parameters
RectTransform.Axis | axis |
Returns
LengthOrAuto |
- (LengthOrNone) cssMax(RectTransform.Axis)
Convenience method for Flex: cssMaxWidth OR cssMaxHeight - Flex can switch axis arbitrarily
Parameters
RectTransform.Axis | axis |
Returns
LengthOrNone |
- (Length) cssMin(RectTransform.Axis)
Convenience method for Flex: cssMinWidth OR cssMinHeight - Flex can switch axis arbitrarily
Parameters
RectTransform.Axis | axis |
Returns
Length |
- (Single) CurrentInnerLength(RectTransform.Axis)
CSS defines 'inner size' as 'content (NO padding, NO border, NO margins)'.
This method SHOULD NOT be used during a layout operation / by a layout algorithm unless sizing of this item has already been completed AND written-back via .setSize on the RectTransform! It is only valid/correct after layout has completed and imposed a specific size on this FlexContainer, and then this method INFERS the EFFECTIVE/USED outer-size based on looking at the current literal Unity3D size.
Parameters
RectTransform.Axis | axis |
Returns
Single | Inner size in the specified dimension, as defined by CSS |
- (Single)
CurrentOuterLength(Nullable, RectTransform.Axis)
CSS defines 'outer size' as 'content + padding + border + margins'.
This method SHOULD NOT be used during a layout operation / by a layout algorithm! It is only valid/correct after layout has completed and imposed a specific size on this FlexItem, and then this method INFERS the EFFECTIVE/USED outer-size based on looking at the current literal Unity3D size.
Parameters
Nullable<Single> | parentInnerLength | |
RectTransform.Axis | axis |
Returns
Single | Outer size in the specified dimension, as defined by CSS |
- (void) OnContentSizeChanged()
Invoke this when the content-size of a FlexItem's contents has changed - most commonly: the text of a TMP or UI.Text component has changed, or the Sprite/Texture in a UI.Image has changed.
You can also invoke a method on the current LayoutAlgorithm to do the same thing, but it requires more steps and manual checks.
Exceptions
Exception |
- (void) OnDisable()
NOTE 1: Bug in the UnityEditor: this method WILL NOT BE CALLED if the Editor is in PlayMode, even though the contents of the method are essential to maintain data-integrity (this method is the ONLY method that Unity officially provides for implementing the callback "Component Was Added To GameObject").
NOTE 2: Bug in Unity: Unity broke their build-system if you use Reset() or OnValidate() on any UIBehaviour subclass. I don't know why Unity did this, there are no comments in the source code, and no explanation in the docs - they just appear to have had no idea what they were doing.
(The UnityEditor DLL and the UnityEngine DLL have incompatible definitions of those methods - which should not be possible!) The workaround we use for now:
- For source builds, we default to doing it correctly (IF UNITY_EDITOR will always be true)
- DLL builds of the project (e.g. the free LITE version) need special multiple DLLs of same name, but different asmdef 'compile for platform' settings
- (void) OnDisable()
NOTE 1: Bug in the UnityEditor: this method WILL NOT BE CALLED if the Editor is in PlayMode, even though the contents of the method are essential to maintain data-integrity (this method is the ONLY method that Unity officially provides for implementing the callback "Component Was Added To GameObject").
NOTE 2: Bug in Unity: Unity broke their build-system if you use Reset() or OnValidate() on any UIBehaviour subclass. I don't know why Unity did this, there are no comments in the source code, and no explanation in the docs - they just appear to have had no idea what they were doing.
(The UnityEditor DLL and the UnityEngine DLL have incompatible definitions of those methods - which should not be possible!) The workaround we use for now:
- For source builds, we default to doing it correctly (IF UNITY_EDITOR will always be true)
- DLL builds of the project (e.g. the free LITE version) need special multiple DLLs of same name, but different asmdef 'compile for platform' settings
- (void) OnEnable()
NOTE 1: Bug in the UnityEditor: this method WILL NOT BE CALLED if the Editor is in PlayMode, even though the contents of the method are essential to maintain data-integrity (this method is the ONLY method that Unity officially provides for implementing the callback "Component Was Added To GameObject").
NOTE 2: Bug in Unity: Unity broke their build-system if you use Reset() or OnValidate() on any UIBehaviour subclass. I don't know why Unity did this, there are no comments in the source code, and no explanation in the docs - they just appear to have had no idea what they were doing.
(The UnityEditor DLL and the UnityEngine DLL have incompatible definitions of those methods - which should not be possible!) The workaround we use for now:
- For source builds, we default to doing it correctly (IF UNITY_EDITOR will always be true)
- DLL builds of the project (e.g. the free LITE version) need special multiple DLLs of same name, but different asmdef 'compile for platform' settings
- (void) OnEnable()
NOTE 1: Bug in the UnityEditor: this method WILL NOT BE CALLED if the Editor is in PlayMode, even though the contents of the method are essential to maintain data-integrity (this method is the ONLY method that Unity officially provides for implementing the callback "Component Was Added To GameObject").
NOTE 2: Bug in Unity: Unity broke their build-system if you use Reset() or OnValidate() on any UIBehaviour subclass. I don't know why Unity did this, there are no comments in the source code, and no explanation in the docs - they just appear to have had no idea what they were doing.
(The UnityEditor DLL and the UnityEngine DLL have incompatible definitions of those methods - which should not be possible!) The workaround we use for now:
- For source builds, we default to doing it correctly (IF UNITY_EDITOR will always be true)
- DLL builds of the project (e.g. the free LITE version) need special multiple DLLs of same name, but different asmdef 'compile for platform' settings
- (void) OnEnable_ConsiderRelayouting()
Unity has a bug: there is literally no way to know if OnEnable is being caused INSIDE an Instantiate call (at which time, by definition, Unity does not allow the MonoBehaviour to be fully valid), or if it's being called because the MonoBehaviour has been enabled - this is a bug they designed-in and completely failed to provide any workarounds for.
This is worse because Unity also has major design mistake: it is impossible to instantiate a MonoBehaviour if that has any important parameters -- Unity removed the core concept of 'Constructors' from the C# language rather than implement their Serializer correctly.
This method is invoked during OnEnable, after any internal checks and performance tweaks have been done to filter-out 'definitely ignorable' OnEnable calls. What is left is situations where 'potentially' a relayout is needed; this method processes those situations and decides what to do
For FlexItem:
- we check whether this component is already inside a FlexboxContext (i.e. a chain that terminates in a RootFlexContainer at the top of the hierarchy) -- and if not, we skip doing the layout
- (void) OnFlexPropertyPathModified(String)
Called by internal methods any time a property of this FlexItem is changed, so that a layout can automatically be scheduled.
You can also call this manually to effectively 'force' a refresh of this FlexItem and its parent FlexContainer,
but usually for that you want to use OnContentSizeChanged() instead, and in most other cases it is
much better to invoke a method on the current LayoutAlgorithm to do the same
thing, since you will have more control over the results. Current algorithms use subclasses of
Parameters
String | bindingPath | Can be a property name, or a dot-separated property-path |
Exceptions
Exception |
- (void) OnRectTransformDimensionsChange()
Unfortunately Unity calls this at random times for no reason - Unity failed to provide any info on what has changed or why, and they kill the call-stack, so there's no way to know what they've broken. According to the docs: "The call is also made to all child rect transforms, even if the child transform itself doesn't change - as it could have, depending on its anchoring." This is bad programming: someone at Unity deliberately broke this callback rather than implement their own code correctly (they should be processing sub-calls themselves, not breaking a public API so that they can get away with doing less work).
We override this to disable it (since this is the only FULLY WORKING way to prevent bugs in Unity's UI system from causing incorrect layout calls) while still preserving the callbacks for correct layout calls (Unity's implementation of UIBehaviour and ILayoutGroup etc has never WORKED, and instead of fixing the bugs they gave up and started work on UIToolkit, which has even more bugs and problems).
Flexbox will NEVER trigger this callback: we circumvent it ... so if this callback DOES fire, that means it's being fired by Unity, and we can deduce that it's a 'change' we want to block and/or re-instate the correct / cached values of the layout tree.
Unity's description: "This callback is called if an associated RectTransform has its dimensions changed."
- (void) OnTransformParentChanged()
Automatically resizes self:
- If this is an RFC, and its new parent is a RectTransform outside of Flexbox, then we do a RectTransform expand-to-fit
- Else: to fix Unity's own bugs, for 2019 + early 2020 etc: if its new parent is a FlexContainer, we trigger a relayout for that flexcontainer.
- (void) Reset()
NOTE 1: Bug in the UnityEditor: this method WILL NOT BE CALLED if the Editor is in PlayMode, even though the contents of the method are essential to maintain data-integrity (this method is the ONLY method that Unity officially provides for implementing the callback "Component Was Added To GameObject").
NOTE 2: Bug in Unity: Unity broke their build-system if you use Reset() or OnValidate() on any UIBehaviour subclass. I don't know why Unity did this, there are no comments in the source code, and no explanation in the docs - they just appear to have had no idea what they were doing.
(The UnityEditor DLL and the UnityEngine DLL have incompatible definitions of those methods - which should not be possible!) The workaround we use for now:
- For source builds, we default to doing it correctly (IF UNITY_EDITOR will always be true)
- DLL builds of the project (e.g. the free LITE version) need special multiple DLLs of same name, but different asmdef 'compile for platform' settings