Skip to main content

Layout Groups

A layout group positions and sizes the children of an element for you: in a row, in a column, or in a grid when the rows wrap. Use one for lists, toolbars, menus, inventories and anything else whose children should stay evenly arranged as they are added, removed and resized.

Three layouts: a column of rows stretched to the width of their parent, two toolbars whose four and three buttons share the toolbar's width, and a grid of squares whose last row is centered. In each layout, the lightest child comes first

Creating a Layout Group​

A layout group is a component on an entity that also has an element, usually a group element. The element's rectangle is the space its children are arranged in. This list stacks five rows from the top down, and stretches each one to the width of the list:

const list = new pc.Entity('list');
list.addComponent('element', {
type: pc.ELEMENTTYPE_GROUP,
anchor: [0.5, 0.5, 0.5, 0.5],
pivot: [0.5, 0.5],
width: 300,
height: 400
});
list.addComponent('layoutgroup', {
orientation: pc.ORIENTATION_VERTICAL,
spacing: [0, 10],
padding: [10, 10, 10, 10],
widthFitting: pc.FITTING_STRETCH
});
screen.addChild(list);

for (let i = 0; i < 5; i++) {
const row = new pc.Entity(`row ${i}`);
row.addComponent('element', {
type: pc.ELEMENTTYPE_IMAGE,
height: 60,
color: new pc.Color(0.23, 0.55, 1)
});
list.addChild(row);
}

Register pc.LayoutGroupComponentSystem and pc.LayoutChildComponentSystem when you create the application. A layout group needs the layout child system even when no child uses it.

How Children Are Placed​

A layout group arranges its direct children that are enabled and have an enabled element, in their order in the hierarchy. For each child, it:

  • Sets the anchor to 0, 0, 0, 0, the bottom-left corner of the group. An anchor you give the child, split or not, is replaced.
  • Sets the position. The layout places the child's rectangle, taking its pivot into account, so the rectangle lands in the same place whatever the pivot.
  • Sets the calculated size when fitting or a layout child changes the child's size. The width and height of the child stay as you set them, and are the size it starts from. See Width and Height.

Entities further down the hierarchy are not affected, so a child can hold its own content, and even a layout group of its own. Nested layout groups are laid out from the outermost in.

The layout is recalculated later in the same frame, before it is drawn, whenever a child is added, removed, enabled, disabled or resized, a child's pivot changes, or a property of the layout group changes. Moving a child yourself does not trigger a layout, and the next layout moves it back. After each layout, the layout group fires reflow with the bounds of its children:

list.layoutgroup.on('reflow', ({ bounds }) => {
// bounds.x and bounds.y are the bottom-left corner of the children, relative to the
// bottom-left corner of the group, and bounds.z and bounds.w are their width and height
console.log(`The children take up ${bounds.z} × ${bounds.w}`);
});

Layout Group Properties​

Orientation​

Horizontal places the children in a row, from left to right. Vertical places them in a column, from the top down.

Reverse​

Reverse X and Reverse Y reverse the order along each axis. Reverse Y is on by default, which is what makes columns, and the rows of a grid, run from the top down. Turn it off to build upwards from the bottom, and turn Reverse X on to build from right to left.

Alignment​

Alignment places the children as a whole inside the group when they don't fill it, from 0, 0 for the bottom-left corner to 1, 1 for the top-right. The default of 0, 1 puts them at the top left. In a grid it also aligns each row, so 0.5, 1 centers a last row that is shorter than the others.

Padding​

Padding is the space kept clear inside the edges of the group, in the order left, bottom, right, top.

Spacing​

Spacing is the gap between neighboring children. Its x value is the gap between the children in a row, and its y value the gap between the children in a column and between the rows of a grid.

Fitting​

Width Fitting and Height Fitting decide whether the layout group changes the sizes of its children to fit the group:

FittingChildren are
NoneLeft at their own size
StretchGrown to fill the group when they are smaller than it, up to any maximum size
ShrinkShrunk to fit the group when they are larger than it, down to any minimum size
BothStretched or shrunk, whichever fits

Along the direction of the layout, such as the width of a row, the free space or the overflow is shared between the children, equally unless layout children give them different proportions. Across the layout, each child is stretched or shrunk to the height of its row, or the width of its column, on its own. The Engine constants are pc.FITTING_NONE, pc.FITTING_STRETCH, pc.FITTING_SHRINK and pc.FITTING_BOTH.

Wrap​

With Wrap on, a child that would overflow the row starts a new one, which makes a grid. The width of the group decides how many children fit on a row: three children 100 units wide with 10 units of spacing need a group at least 320 units wide. In a vertical layout, the height of the group decides how many children fit in each column instead.

Layout Children​

A layout child component on a child of a layout group changes how the layout sizes that child:

PropertyEffect
Min Width, Min HeightThe smallest size the layout gives the child
Max Width, Max HeightThe largest size the layout gives the child. Empty means no limit
Fit Width Proportion, Fit Height ProportionHow the free space or the overflow is shared when the layout stretches or shrinks. Stretching gives a child with 2 twice the extra space of a child with 1. Shrinking takes less from a larger proportion: two 100-unit children with 2 and 1, shrunk into 140 units, become 80 and 60
Exclude from LayoutLeaves the child out of the layout. It keeps its own anchor and position

In a row of buttons that stretch to fill a toolbar, for example, a maximum width stops one of them from growing:

button.addComponent('layoutchild', {
maxWidth: 120
});

Example Layouts​

The layouts in the image at the top of this page use these properties:

PropertyVertical listToolbarGrid
OrientationVerticalHorizontalHorizontal
Alignment0, 10, 0.50.5, 1
Padding10, 10, 10, 1010, 10, 10, 100, 0, 0, 0
Spacing0, 1010, 010, 10
Width FittingStretchStretchNone
Height FittingNoneStretchNone
WrapOffOffOn
  • Vertical list. Each row sets only its height. Width Fitting stretches the rows to the width of the list, less its padding, as in a leaderboard or a settings menu.
  • Toolbar. Stretch fitting on both axes shares the width of the toolbar between the buttons and gives them its height, less the padding, so the buttons keep an even share as buttons are added or removed.
  • Grid. The children are 100 units square and the group is 320 units wide, so three fit on each row. The alignment of 0.5, 1 starts the grid at the top and centers each row, including a last row that is not full.
Layout Group

Changing a Layout at Runtime​

Changing a property of a layout group lays its children out again. Properties that hold vectors need a new vector object:

list.layoutgroup.spacing = new pc.Vec2(0, 20);
list.layoutgroup.padding = new pc.Vec4(20, 20, 20, 20);
list.layoutgroup.wrap = true;

Adding, removing, enabling or disabling a child also lays the group out again, so a list grows as you add rows to it:

const row = new pc.Entity('row');
row.addComponent('element', {
type: pc.ELEMENTTYPE_IMAGE,
height: 60
});
list.addChild(row);

A layout group does not resize its own element to fit its children. To size a scroll view's content to fit a list, use the bounds from the reflow event.

See Also​