-
Notifications
You must be signed in to change notification settings - Fork 23
SGUI Layout System
The layout system provides an easy way to layout controls without having to manually provide a bunch of positions and sizes. It works using the same principles most layout systems use, i.e. paddings, margins and different sizing units.
Units can be found under SGUI.Layout.Units
.
Defining padding and margins requires using the Spacing
unit. This unit takes 4 single-value units representing the left, up, right and down respectively. Number values are auto-converted to instances of the Absolute
unit.
local Padding = Spacing( 0, 5, 0, 5 )
A Spacing.Uniform
helper is provided as a shortcut when every direction is identical:
-- This is equivalent to Spacing( 5, 5, 5, 5 )
local Padding = Spacing.Uniform( 5 )
To define sizes using units, make use of the UnitVector
unit. Number values are auto-converted to instances of the Absolute
unit.
local AutoSize = UnitVector( Percentage( 100 ), 5 )
A UnitVector.Uniform
helper is provided as a shortcut when both directions are identical:
-- This is equivalent to UnitVector( 5, 5 )
local AutoSize = UnitVector.Uniform( 5 )
The default set of single-value units are:
-
Absolute
- Represents an absolute value. Does not change the input value. -
Auto
- Represents automatic sizing based on the contents of the element. This requires the control using it to implementGetContentSizeForAxis( Axis )
(see theauto_sizable_text.lua
mixin for an example).- Labels implement this by returning the width/height of their text.
- Text entries implement this for the Y-axis by returning the height of the font they are configured with.
- Any control using a directional layout (horizontal/vertical layouts) implement this using the layout's known content size, calculated during layout.
- Controls with no layout implement this by adding the size of all children along the given axis.
-
GUIScaled
- Takes the input, and callsSGUI.LinearScale()
on it when being computed.- This used to use
GUIScale
, but that gets modified by HUD scaling options and thus is no longer used.
- This used to use
-
HighResScaled
- When the screen resolution is larger than 1920x1080, this will callSGUI.LinearScale()
on the value when being computed. If the screen resolution is 1920x1080 or less, it just returns the value with no scaling. -
Integer
- Wraps another unit, returning its value as an integer (rounded up). -
Max
- Returns the maximum value out of all units it is constructed with. Allows for defining constraints on sizes, such as the maximum width out of two auto-sized buttons. -
Min
- Returns the minimum value out of all units it is constructed with. -
MultipleOf2
- Wraps another unit, returning the nearest multiple of 2 (rounded half-up) to the value computed from the wrapped unit value. -
Scaled
- Takes a value and a scale. When computed, returnsValue * Scale
. -
Percentage
- Represents a percentage based on the size of the parent on the axis the value is provided for. That is,Percentage( 80 )
would represent 80% of the parent's width if used as a width, or 80% of the parent's height if used as a height. -
OppositeAxisPercentage
- Represents a percentage based on the size of the parent on the opposite axis. That is,OppositeAxisPercentage( 80 )
would represent 80% of the parent's height if used as a width, or 80% of the parent's width if used as a height. -
PercentageOfElement
- Represents a percentage of another element's size, usually an ancestor. The axis by default matches the axis the value is used with, but can be overridden if desired.
Shine comes with two layouts by default, Horizontal
and Vertical
. You are free to compose layouts inside each other
to create whatever nested layout you wish.
Generally, layouts are intended to be used directly against a given control, or as nested children where there is no need for an additional element (e.g. for static content with no scrolling).
The Row
and Column
controls provide a convenient element wrapper around horizontal and vertical layouts
respectively which can be used as children of scrollable panels.
In most cases, SGUI:BuildTree
is the easiest way to work with layouts (and elements in general). To add a layout,
specify the Type
as "Layout"
and then Class
as the desired layout direction, e.g.
SGUI:BuildTree( {
Parent = self,
{
Class = "Vertical",
Type = "Layout",
Props = {
Padding = Spacing.Uniform( HighResScaled( 8 ) )
},
Children = {
-- Add more layouts or elements as children of the layout...
}
}
} )
To manually construct a layout, create it as follows:
local Layout = Shine.GUI.Layout:CreateLayout( Name, ParametersTable )
The parameters table should look something like this:
{
Elements = {}, -- A table containing your objects/layouts that live inside this one
Pos = Vector2( 0, 0 ), -- Usually, position can be left out, as it will be dependent on padding.
AutoSize = UnitVector( Percentage( 100 ), Percentage( 100 ) ), -- AutoSize determines how to size this layout when it's part of another layout.
Size = Vector2( 0, 0 ), -- Sets the absolute size of the layout, usually determined by the parent.
Margin = Spacing( 0, 0, 0, 0 ), -- Sets the space between this layout and other elements of its parent layout.
Padding = Spacing( 0, 0, 0, 0 ), -- Sets how much space to add inside the layout.
Parent = Element, -- Sets the layout's parent element.
Fill = true, -- Sets whether the layout should fill the space of its container, if omitted this defaults to true.
Alignment = SGUI.LayoutAlignment.MIN, -- Sets the alignment of this layout. Applies only if the layout is nested inside another layout.
CrossAxisAlignment = SGUI.LayoutAlignment.MIN -- Sets the alignment on the cross-axis. Applies only if the layout is nested inside another layout.
}
To assign a layout to an SGUI control, just use:
Control:SetLayout( Layout )
The control will now manage the layout. The layout will inherit the control's size (minus any padding set), and whenever the control's layout is invalidated, so too will the layout object's.
Elements inside a layout can have their size updated dynamically. This is achieved through one of two ways.
Setting an element to fill, by calling Control:SetFill( true )
(or setting the Fill = true
prop) means that it will
take up any remaining space in a layout once all other non-filling elements have been positioned. If there are multiple
fill elements in a layout, then they will evenly split the remaining space between them, so that they are all the same
size.
Alternatively, you can specify a dynamic size for an element by setting its AutoSize
property. This should be a
UnitVector
containing your size units. For example:
Control:SetAutoSize( UnitVector( Percentage.ONE_HUNDRED, GUIScaled( 32 ) ) )
would set the control to take 100% of the layout width, and be GUIScale( 32 )
units tall, where the scale is
calculated at the moment the layout is.
If you do not set a control to fill or have an automatic size, then it will not have its size altered by the layout.
Layouts also support alignment. There are currently 3 forms of alignment for layouts: MIN
, MAX
and CENTRE
.
For vertical, MIN
means the element will be positioned from the top, while MAX
means it will be positioned from the
bottom. For horizontal, MIN
means the left and MAX
means the right. CENTRE
starts from the middle of the given
direction, aligning elements symmetrically around the central point.
To set a control or layout's alignment, use:
Control:SetAlignment( SGUI.LayoutAlignment.MIN )
or set the Alignment
prop when building the tree.
In addition to aligning along the main axis of a layout, the alignment along the "cross-axis" (the opposite direction)
can be specified. The same alignment enum is used with the CrossAxisAlignment
property, e.g.
Control:SetCrossAxisAlignment( SGUI.LayoutAlignment.CENTRE )
The layout system centres around invalidation. At a given moment, the layout of an object is considered valid until it
is invalidated by a call to Control:InvalidateLayout()
. When this call is made, the control will run its
PerformLayout()
method on the next frame.
If you want a control or a layout to update itself immediately, use Control:InvalidateLayout( true )
. There's also Control:InvalidateParent( [true] )
for a shortcut to invalidating the parent's layout.
Most major property changes will already cover layout invalidation. Changing an element's size, its padding or margin, or adding/removing elements from a layout all trigger invalidation for you.