Layout Managers#
Layout manager is an object that manages placement and size of views inside containers.
Detailed Description#
In AUI, layout building consists of layout managers. Layout manager determines position and size of container's children views. A container is a AView "view" that consists of other views, called children. In general, layout manager does not allow going beyond the border of the container. A container can be a child of an another container i.e., nesting is allowed.
The ALayout is the base class for all layout managers in AUI. Layout managers are responsible for:
- Positioning child views within their container
- Calculating minimum sizes
- Handling view additions and removals
- Managing spacing between views
- Respecting view margins and alignment
- Supporting expanding/stretching of views
Note
You can use AUI Devtools to play around with layouts, especially with Expanding property, to get better understanding on how does layout work in AUI.
Common layout managers include:
- AHorizontalLayout - Arranges views in a horizontal row
- AVerticalLayout - Arranges views in a vertical column
- AStackedLayout - Centers views, displaying them on top of each other
- AAdvancedGridLayout - Arranges views in a grid with customizable cell sizing
Key concepts:
- Minimum Size - Layout managers calculate minimum size requirements by:
- Considering minimum sizes of child views
- Adding margins and spacing
- Respecting fixed size constraints
-
Following AUI Box Model.
-
Expanding Views - Children can expand to fill available space of their parent:
- Set via
AView::setExpanding()
orass::Expanding
on a child - Requires parent to have
ass::FixedSize
orass::MinSize
or Expanding set to take effect - Independent for horizontal/vertical directions
-
Ignored if
ass::FixedSize
is set -
Spacing - Configurable gaps between views:
- Set via
ALayout::setSpacing()
orass::LayoutSpacing
of the parent view - Part of minimum size calculations of the parent view
-
Applied uniformly between its child views
-
Margins - Space around individual views:
- Set per-view via ASS or margins property
- Respected during layout
-
Part of minimum size calculations of the parent view
-
Layout Direction - Overall flow direction:
- Horizontal layouts flow left-to-right
- Vertical layouts flow top-to-bottom
-
Grid layouts use both directions
-
Relativeness - children position is relative to parent's position, not an absolute position within a window.
-
Nesting - you can nest containers into containers, and so on. When we say "container", it means a AViewContainer. When we say "Vertical", we imply a AViewContainer with AVerticalLayout as the layout manager.
Layout Examples#
Horizontal layout:
Code | Result |
---|---|
![]() |
Vertical layout:
Code | Result |
---|---|
![]() |
Since container can be child of other container, we can create complex UIs using basic layout managers:
Code | Result |
---|---|
![]() |
Stacked layout:
Code | Result |
---|---|
|
 |
Expanding#
Expanding (often referred as stretch factor) is a property of any AView. Expanding is an expansion coefficient set on per-axis basic (i.e, one value along x axis, another value along y axis), however it's convenient to set both values. Hints layout manager how much this AView should be extended relative to other AViews in the same container.
Note
You can use AUI Devtools to play around with layouts, especially with Expanding property, to get better understanding on how does layout work in AUI.
Horizontal layouts ignore y expanding of their children, Vertical layouts ignore x expanding of their children.
Views are normally created without any expanding set. When Expanding views appear in a layout they are given a share of space in accordance with their expanding or their minimum size whichever gives more space to them. Expanding is used to change how much space views are given in proportion to one another.
Expanding view does not affect parent's size or parent's expanding property. Use AView::setExpanding() on parent, or
Expanding
variant of declarative container notation (Vertical::Expanding
, Horizontal::Expanding
,
Stacked::Expanding
) for such case.
Expanding views use free space of their container to grow.
Free space of a container is determined by its size subtracted by sum of minimum sizes of its children. Please note that your container would probably occupy minimum possible size (determined by minimum sizes of its children). It order to make container larger than minimum possible size, you can specify FixedSize or MinSize or Expanding to the container.
You can use ass::Expanding ASS property, or AView::setExpanding method to specify Expanding:
Code | Result |
---|---|
![]() |
Expanding views push remaining views in their container:
Code | Result |
---|---|
![]() |
Expanding view does affect expanding environment inside a single container. If there's one view with expanding set to any positive value it would occupy all free space in the container. If there is a view with expanding equal to 1 and another view with expanding equal to 2 the first view would occupy one third of free space, the second view would occupy two thirds of free space:
Vertical {
_new<AButton>("Left") AUI_LET { it->setExpanding(1); },
_new<AButton>("Right") AUI_LET { it->setExpanding(2); }, // will be twice as big as "Left"
}
You can use ASpacerExpanding as blank expanding view:
Vertical {
_new<AButton>("Left"),
SpacerExpanding(),
_new<AButton>("Right"),
}
Note
FixedSize nullifies Expanding's action (on per axis basic).
Implementation details#
The process of applying position and size involves several key functions:
AWindow::redraw()
└─> AWindow::applyGeometryToChildrenIfNecessary()
└─> AWindow::applyGeometryToChildren()
└─> ALayout::onResize() ┐
└─> AViewContainerBase::getMinimumSize() ┐ │
└─> AViewContainerBase::getContentMinimumWidth() │ │
└─> ALayout::getMinimumWidth() │ │
└─> AView::getMinimumWidth() │ cached │
└─> AViewContainerBase::getContentMinimumHeight() │ │ potentially
└─> ALayout::getMinimumHeight() │ │ recursive
└─> AView::getMinimumHeight() ┘ │
└─> AViewContainerBase::setGeometry() │
└─> AViewContainerBase::setSize() │
└─> AViewContainerBase::applyGeometryToChildrenIfNecessary() │
└─> AViewContainerBase::applyGeometryToChildren() │
└─> ALayout::onResize() ┘
└─> AView::setGeometry()
Applying size#
- Size of each view in tree is calculated on this phase
- AView::redraw - geometry is applied before rendering
- applyGeometryToChildrenIfNecessary - applies geometry only if really needed (i.e., if there were a resize event, or views were added or removed)
- applyGeometryToChildren - applies geometry to its children with no preconditions
ALayout::onResize()
- implemented by layout manager, whose have their own algorithms of arranging viewsAView::setGeometry()
- sets geometry of a view (which might be a container)
Size calculation#
- Layout manager queries Minimum size which is determined with
AView::getMinimumSize()
and cached until the view or its children callAView::markMinContentSizeInvalid()
. It considers:- Children's minimum sizes (if any). A child includes its
ass::Padding
to its minimum size. - Children's
ass::Margin
- Container's
ass::Padding
- Container's
ass::LayoutSpacing
- Other constraints such as
ass::FixedSize
- Children's minimum sizes (if any). A child includes its
- After minimum sizes of children are calculated, layout manager queries their expanding ratios, and gives such views a share of free space if available. Unlike minimum size, Expanding ratio does not depend on children's Expanding ratios.
Special cases#
- AScrollArea: requires special handling for viewport positioning and size compensation
- AForEachUI: manages view inflation/deflation based on visibility
- Performance Optimizations: Views outside viewport may be left unupdated to improve performance
Related Pages#
Grid layout. Unlike AGridLayout, cells may have different sizes.
Base class for all layout managers.
Places views in a stack (along z axis).
Places views in a row.
Absolute positioning layout. Allows to explicitly set your own coordinates.
Places views in a column.
Grid layout with fixed-size cells.
Imitates behaviour of word wrapping, but uses views instead words