mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
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
This commit is contained in:
parent
c79d5e8459
commit
94a3ad1614
15 changed files with 4911 additions and 0 deletions
478
09_intuition/frameworks/mui/05-layout-system.md
Normal file
478
09_intuition/frameworks/mui/05-layout-system.md
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# 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
|
||||
|
||||
```c
|
||||
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:**
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
```c
|
||||
WindowContents, HGroup,
|
||||
Child, SimpleButton("OK"),
|
||||
Child, SimpleButton("Cancel"),
|
||||
End,
|
||||
```
|
||||
|
||||
**Visual result:**
|
||||
|
||||
```mermaid
|
||||
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:
|
||||
|
||||
```c
|
||||
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:**
|
||||
|
||||
```mermaid
|
||||
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.
|
||||
|
||||
```c
|
||||
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.
|
||||
|
||||
```c
|
||||
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:
|
||||
|
||||
```c
|
||||
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:
|
||||
|
||||
```c
|
||||
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:
|
||||
|
||||
```c
|
||||
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:
|
||||
|
||||
```c
|
||||
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):**
|
||||
|
||||
```mermaid
|
||||
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:
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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:
|
||||
|
||||
```c
|
||||
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:**
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
```mermaid
|
||||
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:
|
||||
|
||||
```mermaid
|
||||
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:**
|
||||
|
||||
```c
|
||||
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](04-core-concepts.md)
|
||||
Next: [Widgets Overview](06-widgets-overview.md)
|
||||
Loading…
Add table
Add a link
Reference in a new issue