amiga-bootcamp/09_intuition/frameworks/mui/05-layout-system.md
Ilia Sharin 94a3ad1614 doc: MUI framework documentation — whitepaper overview, SDK-derived architecture, layout mockups
- 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
2026-04-23 16:46:58 -04:00

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 call MUI_Layout() for every child. The rectangle you are given is (0, 0, width-1, height-1).
  • Return MUILM_UNKNOWN for 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