- README.md: comprehensive whitepaper-style overview with historical context, paradigm shift analysis, BOOPSI comparison, version history (Stefan Stuntz as sole author 1.0-3.8), licensing model breakdown, parallel evolution timeline (NeXTSTEP/Qt/MUI convergence), community-sourced developer values - 02-architecture.md: complete rewrite from MUI 3.8 SDK sources — object lifecycle state machine, three-level resource binding, method dispatch chain, notification system with sequence diagrams, layout engine internals (3-pass constraint system), input handling, dynamic object linking, rendering model, tag ID namespace - 05-layout-system.md: Mermaid visual mockups for VGroup, HGroup, nested groups, column grids, scrollgroups, file requester real-world example, layout algorithm and resize sequence diagrams - frameworks/README.md: framework index with comparison table - All content in American English
12 KiB
← Home · Intuition · Frameworks
Layout System
Overview
MUI handles widget positioning automatically through its Group classes. Rather than specifying absolute coordinates, you declare the structure of your UI and MUI computes sizes and positions based on:
- Each widget's minimum, default, and maximum size requirements
- Group direction (horizontal or vertical)
- Spacing, framing, and balancing hints
- User preferences from the MUI settings program
Group Classes
Groups are containers that arrange their children. There are three primary group macros:
| Macro | Direction | Description |
|---|---|---|
VGroup |
Vertical | Stacks children top-to-bottom |
HGroup |
Horizontal | Stacks children left-to-right |
GroupObject |
Configurable | Defaults to vertical; can be changed with MUIA_Group_Horiz |
Basic Vertical Layout
WindowContents, VGroup,
Child, TextObject,
MUIA_Text_Contents, "First line",
End,
Child, TextObject,
MUIA_Text_Contents, "Second line",
End,
Child, StringObject,
StringFrame,
MUIA_String_Contents, "Input here",
End,
End,
Visual result:
graph TB
T1["Text: First line"] --> T2["Text: Second line"] --> S1["String: Input here"]
style T1 fill:#e8f4fd,stroke:#2196f3,color:#333
style T2 fill:#e8f4fd,stroke:#2196f3,color:#333
style S1 fill:#fff3e0,stroke:#ff9800,color:#333
linkStyle default stroke:#ccc,stroke-width:1px
VGroup stacks children top-to-bottom. Each child gets the full window width.
Basic Horizontal Layout
WindowContents, HGroup,
Child, SimpleButton("OK"),
Child, SimpleButton("Cancel"),
End,
Visual result:
graph LR
OK["OK"] --- CANCEL["Cancel"]
style OK fill:#e8f5e9,stroke:#4caf50,color:#333
style CANCEL fill:#ffebee,stroke:#f44336,color:#333
HGroup arranges children left-to-right. Both buttons share available width equally.
Nested Groups
Complex layouts are built by nesting groups:
WindowContents, VGroup,
Child, TextObject,
TextFrame,
MUIA_Background, MUII_TextBack,
MUIA_Text_Contents, "\33cTitle",
End,
Child, HGroup,
Child, VGroup,
Child, SimpleButton("A"),
Child, SimpleButton("B"),
End,
Child, VGroup,
Child, SimpleButton("C"),
Child, SimpleButton("D"),
End,
End,
Child, HGroup,
Child, HSpace(0),
Child, SimpleButton("Close"),
Child, HSpace(0),
End,
End,
Visual result:
graph TB
TITLE["Title"] --> HGROUP["HGroup"]
HGROUP --> A["A"] & C["C"]
A --> B["B"]
C --> D["D"]
TITLE --> BOTTOM["HGroup"]
BOTTOM --> CLOSE["Close"]
style TITLE fill:#e8f4fd,stroke:#2196f3,color:#333
style A fill:#e8f5e9,stroke:#4caf50,color:#333
style B fill:#e8f5e9,stroke:#4caf50,color:#333
style C fill:#e8f5e9,stroke:#4caf50,color:#333
style D fill:#e8f5e9,stroke:#4caf50,color:#333
style CLOSE fill:#fff3e0,stroke:#ff9800,color:#333
style HGROUP fill:#f5f5f5,stroke:#bdbdbd,color:#333
style BOTTOM fill:#f5f5f5,stroke:#bdbdbd,color:#333
linkStyle default stroke:#ccc,stroke-width:1px
The outer VGroup stacks Title → HGroup → Close vertically. The HGroup lays out VGroup1 and VGroup2 side-by-side. A and B stack within VGroup1; C and D within VGroup2.
Spacing Objects
Rectangle
A Rectangle is an invisible spacing object. Use it to create fixed or flexible gaps.
Child, RectangleObject,
MUIA_Rectangle_HMin, 20,
MUIA_Rectangle_VMin, 10,
End,
Balance
A Balance object is a draggable separator that divides space between adjacent children. Users can drag it to resize the areas.
Child, ListviewObject,
MUIA_Listview_List, myList,
End,
Child, BalanceObject, End,
Child, TextObject,
MUIA_Text_Contents, "Details pane",
End,
HSpace and VSpace
Quick macros for adding flexible spacing:
Child, HSpace(0), /* expands to fill available horizontal space */
Child, VSpace(0), /* expands to fill available vertical space */
Use these to push widgets to the edges or center them.
Frames and Backgrounds
Frames are visual borders around objects. MUI provides frame macros that set both the frame style and background:
| Macro | Appearance |
|---|---|
TextFrame |
Standard text field frame |
StringFrame |
Input field frame |
GroupFrame |
Group border with optional title |
ReadListFrame |
Listview frame |
ButtonFrame |
Button frame |
Usage:
Child, TextObject,
TextFrame,
MUIA_Background, MUII_TextBack,
MUIA_Text_Contents, "Framed text",
End,
Backgrounds can be specified with standard MUI images:
| Constant | Meaning |
|---|---|
MUII_BACKGROUND |
Standard background |
MUII_SHADOW |
Shadow color |
MUII_SHINE |
Highlight color |
MUII_FILL |
Fill pattern |
MUII_TEXTBACK |
Text background |
MUII_BUTTONBACK |
Button background |
Group Attributes
Same Size
Force all children to have the same size:
HGroup, MUIA_Group_SameSize, TRUE,
Child, SimpleButton("Short"),
Child, SimpleButton("A much longer label"),
End,
Both buttons will expand to fit the widest label.
Columns
Arrange children in a grid with a fixed number of columns:
GroupObject, MUIA_Group_Columns, 3,
Child, SimpleButton("1"),
Child, SimpleButton("2"),
Child, SimpleButton("3"),
Child, SimpleButton("4"),
Child, SimpleButton("5"),
End,
Visual result (3-column grid, 5 children):
graph TB
subgraph "ColGroup 3"
direction TB
subgraph "Row 1"
direction LR
B1["1"]
B2["2"]
B3["3"]
end
subgraph "Row 2"
direction LR
B4["4"]
B5["5"]
EMPTY[" "]
end
end
style B1 fill:#e8f5e9,stroke:#4caf50,color:#333
style B2 fill:#e8f5e9,stroke:#4caf50,color:#333
style B3 fill:#e8f5e9,stroke:#4caf50,color:#333
style B4 fill:#e8f5e9,stroke:#4caf50,color:#333
style B5 fill:#e8f5e9,stroke:#4caf50,color:#333
style EMPTY fill:none,stroke:none
Horiz
Make a Group horizontal instead of vertical:
GroupObject, MUIA_Group_Horiz, TRUE,
...
End,
Custom Layout Hooks
For complex layouts that MUI's built-in groups cannot express, you can provide a custom layout hook. The hook receives layout messages and positions children manually.
Hook Structure
SAVEDS ULONG __asm LayoutFunc(REG(a0) struct Hook *h,
REG(a2) Object *obj,
REG(a1) struct MUI_LayoutMsg *lm)
{
switch (lm->lm_Type)
{
case MUILM_MINMAX:
/* Calculate and return min/max/default sizes */
lm->lm_MinMax.MinWidth = ...;
lm->lm_MinMax.MinHeight = ...;
lm->lm_MinMax.DefWidth = ...;
lm->lm_MinMax.DefHeight = ...;
lm->lm_MinMax.MaxWidth = MUI_MAXMAX;
lm->lm_MinMax.MaxHeight = MUI_MAXMAX;
return 0;
case MUILM_LAYOUT:
/* Position each child with MUI_Layout() */
Object *cstate = (Object *)lm->lm_Children->mlh_Head;
Object *child;
while (child = NextObject(&cstate))
{
if (!MUI_Layout(child, left, top, width, height, 0))
return FALSE;
}
return TRUE;
}
return MUILM_UNKNOWN;
}
Attaching the Hook
static struct Hook LayoutHook = { {0,0}, LayoutFunc, NULL, NULL };
...
GroupObject,
MUIA_Group_LayoutHook, &LayoutHook,
Child, ...,
Child, ...,
End,
Important Notes
- In
MUILM_MINMAX, the children's min/max values are already calculated. You can use them to derive your group's size. - In
MUILM_LAYOUT, you must callMUI_Layout()for every child. The rectangle you are given is(0, 0, width-1, height-1). - Return
MUILM_UNKNOWNfor any message type you do not handle. - Avoid errors during layout; MUI does not handle them gracefully.
Virtual Groups and Scroll Groups
When content exceeds available space, use a virtual group with scrollbars:
Child, ScrollgroupObject,
MUIA_Scrollgroup_Contents, VirtgroupObject,
Child, /* lots of widgets */,
Child, /* lots of widgets */,
End,
End,
The Virtgroup creates a virtual canvas. The Scrollgroup adds scrollbars as needed.
Scrollgroup structure:
graph TB
subgraph "ScrollgroupObject"
direction TB
subgraph "Visible viewport"
V1["Widget 1"]
V2["Widget 2"]
V3["Widget 3"]
end
VSCROLL["▲ Scrollbar ▼"]
subgraph "Hidden (below viewport)"
V4["Widget 4"]
V5["Widget 5"]
V6["..."]
end
end
style V1 fill:#e8f5e9,stroke:#4caf50,color:#333
style V2 fill:#e8f5e9,stroke:#4caf50,color:#333
style V3 fill:#e8f5e9,stroke:#4caf50,color:#333
style V4 fill:#f5f5f5,stroke:#bdbdbd,color:#999
style V5 fill:#f5f5f5,stroke:#bdbdbd,color:#999
style V6 fill:#f5f5f5,stroke:#bdbdbd,color:#999
style VSCROLL fill:#fff3e0,stroke:#ff9800,color:#333
Layout Algorithm
sequenceDiagram
participant App as Application
participant Win as Window
participant Root as Root Group
participant C1 as Child 1
participant C2 as Child 2
Note over App,C2: Pass 1 — AskMinMax (bottom-up)
Root->>C1: MUIM_AskMinMax
C1-->>Root: min=40, def=80, max=200
Root->>C2: MUIM_AskMinMax
C2-->>Root: min=60, def=100, max=300
Root-->>Win: group min=100, def=180, max=500
Note over App,C2: Window opens at calculated size
Win->>Win: OpenWindow(def_width=180)
Note over App,C2: Pass 2 — Layout (top-down)
Win->>Root: Layout(0, 0, 180, h)
Root->>Root: Distribute 180px by weight
Root->>C1: MUI_Layout(x=0, w=80)
Root->>C2: MUI_Layout(x=82, w=98)
Note over App,C2: Pass 3 — Draw
Root->>C1: MUIM_Draw
Root->>C2: MUIM_Draw
On Window Resize
sequenceDiagram
participant User
participant Win as Window
participant Root as Root Group
participant C1 as Child 1
participant C2 as Child 2
User->>Win: Drags size gadget
Win->>C1: MUIM_Hide
Win->>C2: MUIM_Hide
Win->>Root: Layout(0, 0, new_w, new_h)
Root->>Root: Redistribute space
Root->>C1: MUI_Layout(new rect)
Root->>C2: MUI_Layout(new rect)
Win->>C1: MUIM_Show + MUIM_Draw
Win->>C2: MUIM_Show + MUIM_Draw
File Requester — Real-World Layout Example
From the official MUI SDK, a file requester demonstrates nested group layout:
graph TB
subgraph "Window (VGroup)"
direction TB
subgraph "HGroup — Lists"
direction LR
FILELIST["File List<br/>(Listview)<br/>C/<br/>Classes/<br/>Devs/<br/>..."]
DEVLIST["Device List<br/>(Listview)<br/>dh0:<br/>dh1:<br/>df0:<br/>ram:"]
end
PATH["Path: ___________________"]
FILE["File: ___________________"]
subgraph "HGroup — Buttons"
direction LR
OK["OK"]
SPACE1[" "]
CANCEL["Cancel"]
end
end
style FILELIST fill:#e8f4fd,stroke:#2196f3,color:#333
style DEVLIST fill:#e8f4fd,stroke:#2196f3,color:#333
style PATH fill:#fff3e0,stroke:#ff9800,color:#333
style FILE fill:#fff3e0,stroke:#ff9800,color:#333
style OK fill:#e8f5e9,stroke:#4caf50,color:#333
style CANCEL fill:#ffebee,stroke:#f44336,color:#333
style SPACE1 fill:none,stroke:none
Corresponding MUI code:
VGroup,
Child, HGroup,
Child, FileListview(),
Child, DeviceListview(),
End,
Child, PathGadget(),
Child, FileGadget(),
Child, HGroup,
Child, OkayButton(),
Child, HSpace(0),
Child, CancelButton(),
End,
End;
Previous: Core Concepts Next: Widgets Overview