Native UI Layout Reference¶
This document is the authoritative reference for eepp’s native application UI layout system.
It describes the behavior of the layout primitives used by normal eepp applications such as ecode, eterm, eproc, dialogs, settings panels, forms, workspaces, toolbars, and application-specific tools.
For the recommended high-level authoring model and conventions, read `ui_authoring.md` first.
This document intentionally describes current eepp behavior, not Android, browser, Qt, or other framework behavior that may look similar.
HTML formatting contexts such as block, inline, Flexbox, CSS Grid, table layout, floats, and positioned HTML are a separate system primarily used by UIWebView, UIMarkdownView, and the HTML compatibility layer. They are not covered here except where a distinction is important.
1. Native layout model¶
The normal eepp layout model consists of:
UIWidget size policy margins padding layout gravity min/max constraints optional layout weight optional relative-position relationship UILayout owns or participates in positioning/sizing children specific layouts UILinearLayout UIRelativeLayout UIGridLayout UIFlowLayout UISplitter
The most important idea is:
The parent layout controls the final allocation and position of its children.
A child supplies a sizing/placement contract through properties such as:
layout_width layout_height layout_weight layout_gravity margin min-width max-width min-height max-height
The exact meaning of those properties depends on the parent layout.
2. XML and CSS spelling¶
eepp accepts aliases, but normal project style distinguishes XML attributes from CSS properties.
Preferred XML spelling:
<TextInput layout_width="0dp" layout_height="wrap_content" layout_weight="1" layout_gravity="center_vertical" />
Preferred CSS spelling:
.form-input { layout-width: 0dp; layout-height: wrap_content; layout-weight: 1; layout-gravity: center_vertical; }
Common compact aliases:
layout_width -> lw layout_height -> lh layout_weight -> lw8 layout_gravity -> lg match_parent -> mp wrap_content -> wc
Both full and compact forms are common in production eepp code.
3. Default widget layout state¶
A newly constructed UIWidget starts with:
layout weight = 0 layout gravity = unset / 0 width policy = WrapContent height policy = WrapContent position policy = None
Specific widgets may override their own defaults.
Do not assume every widget behaves identically under wrap_content; intrinsic sizing is widget-specific.
4. Size policies¶
The native size-policy enum is:
enum class SizePolicy { Fixed, MatchParent, WrapContent };
These policies are applied independently to width and height.
4.1 <tt>wrap_content</tt>¶
XML:
layout_width="wrap_content" layout_height="wrap_content"
Compact:
lw="wc" lh="wc"
A WrapContent axis asks the widget to derive its size from its intrinsic/content size.
The exact intrinsic size is widget-specific.
Examples:
text widgets derive size from their text and font metrics;
image widgets derive size from the drawable;
buttons derive size from their internal content/theme;
native layouts may derive their wrapped axis from their visible children.
Padding contributes to normal content sizing where the widget implementation defines it.
wrap_content is the default native size policy for a plain UIWidget.
4.2 <tt>match_parent</tt>¶
XML:
layout_width="match_parent" layout_height="match_parent"
Compact:
lw="mp" lh="mp"
MatchParent requests the available parent content size on that axis.
The generic match-parent calculation is:
parent size - parent content offsets - non-auto child margins
Parent content offsets include padding and, where applicable, borders.
max-width / max-height can cap the resulting match-parent size.
Conceptually:
child match-parent width = parent width - parent left/right content offsets - child left/right margins
The exact layout can perform additional sizing rules around this value.
Do not treat match_parent as a synonym for CSS width: 100%. They may often produce similar geometry, but they belong to different layout systems.
4.3 Fixed size¶
Any explicit dimension normally selects Fixed sizing on that axis:
layout_width="240dp" layout_height="32dp"
The value is resolved and stored as the widget size.
The literal value:
fixed
can also select the fixed policy without supplying a new dimension.
Use explicit sizes where the dimension itself is meaningful. Do not use arbitrary fixed dimensions to compensate for a parent-layout mistake.
4.4 Zero size and weighted LinearLayout children¶
There is one important special case.
Inside a UILinearLayout, the canonical weighted-child pattern intentionally uses zero on the weighted axis:
Horizontal parent:
<TextInput layout_width="0dp" layout_weight="1" />
Vertical parent:
<ListView layout_height="0dp" layout_weight="1" />
When eepp parses a zero layout dimension for a weighted child whose parent is a UILinearLayout, it does not immediately force the stored size in the normal fixed-size path. The linear layout assigns the weighted size during packing.
5. Minimum and maximum constraints¶
Widgets support:
min-width max-width min-height max-height
These can constrain native layout results.
For example, generic match_parent width calculation applies a max-width cap when one exists.
Some native layout algorithms also explicitly fit wrapped container sizes against min/max constraints.
Do not assume min/max handling is identical to browser intrinsic-sizing rules. This document describes the native layout system; HTML layout has additional rules.
6. Margin and padding¶
Margin¶
Margins belong to the child and participate in parent layout allocation/positioning.
Example:
<PushButton margin-left="8dp" />
Margins affect:
linear packing;
match-parent available size;
relative positioning;
flow wrapping;
grid placement;
splitter/container alignment where relevant.
Padding¶
Padding belongs to the container/widget.
Example:
<vbox padding="12dp"> ... </vbox>
Native layouts generally place children inside the padded content area.
Conceptually:
container border box padding child layout area
7. <tt>gravity</tt> and <tt>layout_gravity</tt>¶
These are different properties.
<tt>gravity</tt>¶
gravity controls the widget’s own content or, for layouts that explicitly use it, the layout’s internal arrangement.
Examples include:
left right center_horizontal top bottom center_vertical center
Widget behavior varies because different widget types own different content.
<tt>layout_gravity</tt>¶
layout_gravity describes how the widget should be placed by its parent layout when that layout supports gravity.
XML:
layout_gravity="right|center_vertical"
CSS:
layout-gravity: right|center_vertical;
The distinction is:
gravity content/layout behavior inside this widget layout_gravity placement of this widget by its parent
This distinction is especially important in UILinearLayout.
8. <tt>gravity-owner</tt>¶
Layouts normally respect the positioning policy of their parent.
Some parent widgets advertise that they own child positioning with UI_OWNS_CHILDREN_POSITION.
A layout can set:
gravity-owner: true
to force its own layout_gravity alignment against the parent even when the parent normally owns child positions.
This is an advanced escape hatch and should not be a default solution to layout problems.
9. Layout invalidation and update behavior¶
Native layouts are invalidated when relevant state changes, including:
child count changes;
layout-affecting child attributes;
layout size changes;
padding changes;
parent-size changes.
A dirty layout is normally scheduled through the owning UISceneNode.
During an active layout pass, some updates can occur synchronously to avoid waiting for another frame.
Calling UILayout::getSize() on a dirty layout forces that layout to update before returning its size.
Nested layouts are tracked by their parent UILayout and participate in layout-tree updates.
This matters mainly when implementing custom layout widgets. Ordinary application code should set properties and allow the scene to perform layout.
10. Visibility and native layouts¶
Native layouts generally operate on visible widget children.
Examples:
UILinearLayoutignores invisible children while packing;UIFlowLayoutignores invisible children while creating rows;UIGridLayoutignores invisible children;invisible native layouts may collapse their own layout size to zero during their update path.
Do not use invisible children as spacers.
11. <tt>UILinearLayout</tt>¶
UILinearLayout is the primary sequential application layout.
XML aliases:
<vbox> <hbox>
C++:
UILinearLayout::NewVertical(); UILinearLayout::NewHorizontal();
Default orientation:
vertical
11.1 Vertical linear layout¶
A vertical layout packs visible children from top to bottom.
Each child’s vertical margins contribute to the consumed main-axis space.
The child’s layout_gravity controls horizontal placement inside the layout:
left center_horizontal right
Example:
<vbox layout_width="match_parent" layout_height="match_parent" padding="12dp"> <TextView text="Left" layout_gravity="left" /> <PushButton text="Centered" layout_gravity="center_horizontal" /> <PushButton text="Right" layout_gravity="right" /> </vbox>
11.2 Horizontal linear layout¶
A horizontal layout packs visible children from left to right.
Each child’s horizontal margins contribute to consumed main-axis space.
The child’s layout_gravity controls vertical placement:
top center_vertical bottom
Example:
<hbox layout_width="match_parent" layout_height="40dp"> <TextView text="Name" layout_gravity="center_vertical" /> <TextInput layout_width="0dp" layout_weight="1" layout_gravity="center_vertical" /> </hbox>
12. LinearLayout size-policy behavior¶
Before packing, the layout applies child policies on the cross axis.
Vertical layout¶
For child width:
WrapContent: child auto-sizing is enabled;MatchParent: width becomes the layout’s content width minus child horizontal margins;Fixed: existing width remains.
A child with layout_height="match_parent" and zero weight can also be resized to the parent’s available height.
Horizontal layout¶
For child height:
WrapContent: child auto-sizing is enabled;MatchParent: height becomes the layout’s content height minus child vertical margins;Fixed: existing height remains.
A child with layout_width="match_parent" and zero weight can also be resized to the parent’s available width.
13. LinearLayout weights¶
layout_weight distributes remaining space along the layout orientation.
Canonical horizontal use:
<hbox lw="mp" lh="wc"> <TextView text="Project" min-width="90dp" lg="center_vertical" /> <TextInput lw="0dp" lw8="1" /> </hbox>
Canonical vertical use:
<vbox lw="mp" lh="mp"> <TextView text="Header" /> <ListView lw="mp" lh="0dp" lw8="1" /> <hbox lw="mp" lh="wc"> <!-- footer --> </hbox> </vbox>
13.1 Weight calculation¶
The implementation treats positive weights as relative weights.
For all visible children:
totalWeight = sum(max(childWeight, 0))
The layout first calculates the main-axis space already consumed by:
visible non-weighted fixed/wrapped children;
visible child margins.
Then each positively weighted child gets:
remainingSpace * childWeight / totalWeight
Therefore these are equivalent ratios:
1, 1 0.5, 0.5 50, 50
and:
1, 3 25, 75
produce equivalent proportions.
Current implementation does not require weights to be normalized to 0..1 or sum to 1.
Older documentation that describes weights as normalized should be considered overly restrictive.
13.2 Example calculation¶
Container:
horizontal content width = 600dp
Children:
A = fixed 100dp B = weight 1 C = weight 3
Ignoring margins:
remaining = 600 - 100 = 500 totalWeight = 4 B = 125 C = 375
14. LinearLayout wrap-content behavior¶
A vertical UILinearLayout with layout_height="wrap_content" grows to the visible child heights, vertical margins, and padding.
A horizontal UILinearLayout with layout_width="wrap_content" similarly grows from visible child widths, margins, and padding.
The cross-axis wrapped size is based on the maximum visible non-match-parent child extent plus container padding, with min/max constraints considered.
A wrapped linear layout can repack after its cross-axis size changes because that size may affect child alignment or nested layout.
15. LinearLayout properties¶
Layout-specific:
orientation gravity-owner
orientation values:
vertical horizontal
Default:
vertical
Child properties commonly used with a linear layout:
layout_width layout_height layout_weight layout_gravity margin min-width max-width min-height max-height
16. <tt>UIRelativeLayout</tt>¶
UIRelativeLayout is a native layout for:
alignment against the parent;
positioning one child relative to a sibling.
It owns child positioning.
Use it when sibling relationships are more natural than a nested vbox / hbox hierarchy.
Do not choose it merely because it permits more arbitrary positioning.
17. RelativeLayout child sizing¶
For each widget child:
Width:
WrapContent -> enable auto-sizing MatchParent -> layout width - child horizontal margins - layout horizontal padding Fixed -> keep existing width
Height:
WrapContent -> enable auto-sizing MatchParent -> layout height - child vertical margins - layout vertical padding Fixed -> keep existing height
18. RelativeLayout parent alignment¶
When a child has no sibling-relative position policy, layout_gravity positions it against the UIRelativeLayout.
Horizontal:
left center_horizontal right
Vertical:
top center_vertical bottom
Example:
<RelativeLayout layout_width="match_parent" layout_height="match_parent" padding="8dp"> <PushButton text="Bottom right" layout_gravity="right|bottom" /> </RelativeLayout>
19. RelativeLayout sibling relationships¶
Supported relationships are:
layout_to_left_of layout_to_right_of layout_to_top_of layout_to_bottom_of
The value is the target sibling ID.
Example:
<RelativeLayout layout_width="match_parent" layout_height="match_parent"> <Widget id="sidebar" layout_width="220dp" layout_height="match_parent" /> <Widget id="content" layout_width="match_parent" layout_height="match_parent" layout_to_right_of="sidebar" /> </RelativeLayout>
The relationship is resolved by finding the referenced node and storing the target widget.
It is used only when target and positioned widget share the same parent.
Only one stored PositionPolicy is active at a time.
This is not a general constraint solver.
20. RelativeLayout wrap-content caveat¶
UIRelativeLayout::updateLayout() applies its own size policy and lays out children, but it does not compute a new bounding rectangle from all positioned children.
For application containers, prefer a defined or match_parent size when using UIRelativeLayout.
Do not assume wrap_content behaves like a general constraint-layout “measure descendants and fit” pass.
21. <tt>UIGridLayout</tt>¶
UIGridLayout is eepp’s native repeated-cell grid.
It is not CSS Grid.
Use it for normal application UI when children should occupy repeated cells with a common row/column size policy.
Do not infer CSS track sizing behavior from this class.
22. GridLayout sizing model¶
Grid cells use one column mode and one row mode.
column-mode: size weight row-mode: size weight
Defaults:
column-mode = weight row-mode = weight column-weight = 0.25 row-weight = 0.25 column-width = 0 row-height = 0 column-margin = 0 row-margin = 0
When column-mode=size, cell width is column-width.
When column-mode=weight, cell width is approximately:
available grid width * column-weight
When row-mode=size, cell height is row-height.
When row-mode=weight, cell height is approximately:
available grid height * row-weight
23. GridLayout placement¶
Visible widget children are processed sequentially.
For each child:
determine target cell size;
set child size policy to
Fixed, Fixed;resize child to target size;
place child at current grid position;
advance by cell width plus column margin;
wrap to the next row when the next cell no longer fits.
This is a repeated-cell wrapping grid, not a named-track CSS grid.
24. GridLayout child weight override¶
UIGridLayout has a special behavior:
If a child has non-zero layout_weight, that child’s target width becomes:
child layout weight * grid content width
This is not UILinearLayout ‘s remaining-space distribution algorithm.
Do not assume layout_weight means the same algorithm in every layout type.
For straightforward grids, prefer the layout-level row/column sizing properties unless this per-child width override is intentionally needed.
25. GridLayout alignment¶
The grid’s own horizontal gravity affects row placement:
left center right
This is container gravity, not child layout_gravity.
26. GridLayout wrap-content behavior¶
With:
layout_height="wrap_content"
the layout derives its height from generated rows plus vertical padding.
A match-parent grid width/height is resolved from the parent before cell placement.
Children are converted to fixed cell sizes during grid layout.
27. GridLayout properties¶
column-mode column-weight column-width column-margin row-mode row-weight row-height row-margin
Container gravity also affects horizontal grid alignment.
28. <tt>UIFlowLayout</tt>¶
UIFlowLayout is eepp’s native horizontal wrapping flow layout.
Conceptually:
A B C D E F G H I
Children are consumed in order from left to right.
When the next visible child no longer fits the current row, a new row is created.
29. FlowLayout width behavior¶
A notable design choice:
When the flow layout itself uses:
layout_width="wrap_content"
it initially uses the available match-parent width as its wrapping width.
After layout, if all visible children effectively fit on one row and use less than the available width, the layout can shrink its wrapped width to the used width.
So wrap_content for UIFlowLayout is not simply “sum all child widths without a constraint.” Available parent width participates in wrapping first.
30. FlowLayout row creation¶
For each visible child:
apply the child’s size policy;
test whether the child would exceed the current row;
if necessary, create a new row;
place the child;
include child margins in row consumption;
track the row’s maximum height.
The implementation can also create a new row after placement when the resulting cursor exceeds the layout width.
31. FlowLayout child size policies¶
Before rows are built:
WrapContent width/height enable child auto-sizing MatchParent width available match-parent width MatchParent height available match-parent height Fixed retain explicit size
After row heights are known, a child with:
layout_height="match_parent"
is resized to that row’s maximum height.
32. FlowLayout alignment¶
The flow layout’s own gravity controls row-group placement.
Horizontal gravity:
left center right
controls each row’s horizontal displacement.
Vertical gravity:
top center_vertical bottom
can shift the complete row group inside a container taller than the total flow content.
Within each row, row-valign controls child vertical alignment.
33. <tt>row-valign</tt>¶
Values:
top center bottom
Default:
bottom
Meaning:
top child aligns near row top, respecting top margin center child is centered in row maximum height bottom child aligns near row bottom, respecting bottom margin
This is separate from the container’s vertical gravity.
34. FlowLayout wrap-content height¶
With:
layout_height="wrap_content"
height becomes:
top padding + sum(row maximum heights) + bottom padding
with child vertical margins contributing to row heights.
35. FlowLayout properties¶
Layout-specific:
row-valign gravity-owner
Important general properties:
gravity layout_width layout_height padding margin
UIFlowLayout does not use child layout_weight as a remaining-space distribution mechanism.
36. <tt>UISplitter</tt>¶
UISplitter is a two-pane resizable layout.
Default orientation:
horizontal
Horizontal:
[first pane] | [second pane]
Vertical:
[first pane] ------------- [second pane]
The separator is an internal draggable widget.
37. Splitter child model¶
UISplitter supports at most two external widget children.
Internally it also owns its separator widget.
The first external child becomes the first pane; the second becomes the last pane.
Additional external children are rejected/closed.
Children placed into the splitter are forced to:
Fixed, Fixed
because the splitter owns their exact geometry.
38. Splitter with one child¶
If only one pane exists:
the separator is hidden and disabled;
the first child fills the splitter’s padded content area.
39. Split partition¶
Property:
splitter-partition
Default:
50%
Meaning:
Desired space occupied by the first pane along the splitter axis.
The separator’s own size is removed from available split space when visible.
Conceptually:
totalSpace = splitter content axis size - visible separator size first = resolve(splitter-partition, totalSpace) second = totalSpace - first
40. Splitter minimum sizes¶
The first pane’s minimum size constrains the initial partition.
During dragging, both pane minimum sizes constrain separator movement.
A requested partition may therefore not be achievable when it violates minimum sizes.
Do not assume 0% always produces a physically zero-sized first pane if its minimum size is non-zero.
41. Splitter visibility properties¶
splitter-always-show
Default:
true
When true, the divider remains visible whenever two panes exist.
splitter-hide-on-edge
Default:
false
When enabled and splitter-always-show is false, percentage partitions exactly at 0% or 100% can hide the divider so one pane consumes the full splitter.
42. Splitter dragging¶
Horizontal orientation:
separator drags along x cursor = horizontal resize
Vertical orientation:
separator drags along y cursor = vertical resize
Dragging immediately resizes both children.
After a drag, the stored split partition is recalculated as a percentage of available split space.
43. Splitter properties¶
orientation splitter-partition splitter-always-show splitter-hide-on-edge
Orientation values:
horizontal vertical
44. Choosing the correct native layout¶
Need |
Native layout |
|---|---|
Vertical sequence |
|
Horizontal sequence |
|
Flexible remaining space in a row/column |
|
Child positioned relative to a sibling |
|
Repeated equal/weighted cells |
|
Horizontal items that wrap into rows |
|
Two user-resizable panes |
|
Tabs |
|
Split/tab editor workspace |
|
Web/document formatting |
HTML compatibility layout |
UITabWidget and UITabWidgetSplitter are higher-level containers rather than low-level generic layout algorithms and belong in the widget/application references.
45. Production patterns¶
Flexible form row¶
<hbox layout_width="match_parent" layout_height="wrap_content" margin-bottom="8dp"> <TextView text="Project" min-width="90dp" layout_gravity="center_vertical" /> <TextInput id="project" layout_width="0dp" layout_weight="1" /> </hbox>
Wrapping option group¶
<FlowLayout layout_width="match_parent" layout_height="wrap_content" row-valign="center"> <CheckBox text="Linux" margin-right="8dp" /> <CheckBox text="macOS" margin-right="8dp" /> <CheckBox text="Windows" margin-right="8dp" /> <CheckBox text="FreeBSD" margin-right="8dp" /> </FlowLayout>
Two-pane workspace¶
<Splitter layout_width="match_parent" layout_height="match_parent" orientation="horizontal" splitter-partition="30%"> <TreeView /> <CodeEditor /> </Splitter>
46. Important differences from browser layout¶
Do not import these as semantic equivalences:
match_parent == width:100% layout_weight == flex-grow UIGridLayout == CSS Grid UIFlowLayout == display:flex + flex-wrap layout_gravity == justify-content / align-items
They can be useful analogies, but native eepp layouts are separate algorithms.
47. Important differences from Android intuition¶
eepp deliberately borrows several Android-style concepts, especially:
LinearLayout layout_width layout_height match_parent wrap_content layout_weight layout_gravity RelativeLayout-style sibling relations dp
Android documentation is not authoritative for eepp.
Notable example:
Current eepp UILinearLayout weights are arbitrary relative positive values; they do not need to be normalized or sum to 1.
Always prefer eepp behavior over external analogy.
48. Inspecting native layout at runtime¶
Use the Runtime UI Inspector & Automation Protocol rather than guessing geometry.
Typical process:
ui.contexts ↓ ui.query target ↓ ui.inspect target geometry/layout properties ↓ inspect parent ↓ inspect relevant siblings ↓ screenshot if useful
Stable debugging questions:
What parent layout owns this child? What are its width/height policies? What are its actual bounds? Does it have weight? What are its margins? What is the parent's padding? What gravity is active? What min/max constraints exist?
For weighted linear layouts, inspect all siblings participating in the same axis.
49. Layout debugging checklist¶
When size is wrong:
1. identify parent layout type 2. inspect layout_width / layout_height 3. determine Fixed / MatchParent / WrapContent 4. check layout_weight 5. check margins 6. check parent padding/content offsets 7. check min/max constraints 8. check sibling sizes/weights 9. check visibility 10. only then consider an explicit fixed size
When position is wrong:
1. identify parent layout 2. check layout_gravity 3. distinguish layout_gravity from gravity 4. check margins 5. check RelativeLayout position relationship 6. check parent gravity where Grid/Flow uses it 7. check gravity-owner only if parent positioning ownership matters
50. Implementation-oriented notes¶
These details matter mainly when implementing custom layouts.
Layout dirtiness¶
UILayout maintains dirty state and participates in the scene’s layout invalidation queue.
Reentrancy guard¶
Layouts use mPacking to prevent recursive packing.
Nested layouts¶
A UILayout tracks direct child layouts and updates the layout tree recursively.
Auto-size children¶
A layout can request onAutoSize() for ordinary children or temporarily update a child layout as wrapping content.
Parent notifications¶
When a layout’s wrapped size changes, implementations notify the parent so enclosing layouts can repack.
Custom layout implementations should preserve these lifecycle expectations.
51. Scope boundaries¶
This document intentionally does not specify:
HTML Flexbox;
HTML CSS Grid;
block/inline formatting;
HTML table layout;
floats;
sticky/fixed/absolute HTML positioning;
HTML intrinsic min-content/max-content behavior.
See `ui_html_compatibility.md` for HTML/web layout semantics.
For related topics:
`ui_introduction.md`— introduction to eepp’s UI system.`ui_authoring.md`— recommended application UI mental model and authoring conventions.`ui_css_for_applications.md`— CSS behavior and conventions for normal application widgets.`ui_inspector.md`— runtime UI inspection and automation protocol.`ui_databinding.md`— UI data binding.`ui_charts.md`— charting widgets and chart-specific behavior.
52. Source of truth¶
When uncertain about native layout behavior, use this order:
current eepp implementation
current eepp unit/integration tests
production eepp/ecode/eterm/eproc usage
this reference
`css_specification.md`and other general docsAndroid/browser/other-framework analogy
If a documented rule and the current implementation disagree, treat the implementation as authoritative and update the documentation.