mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-12 16:16: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
|
|
@ -17,6 +17,7 @@ Intuition is the AmigaOS windowing system and user interface manager. It sits be
|
|||
| [idcmp.md](idcmp.md) | IDCMP message classes and IntuiMessage |
|
||||
| [boopsi.md](boopsi.md) | BOOPSI object-oriented gadget system |
|
||||
| [input_events.md](input_events.md) | InputEvent, input.device, commodities |
|
||||
| **[frameworks/](frameworks/)** | **GUI Frameworks: MUI, ReAction, BGUI** |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
24
09_intuition/frameworks/README.md
Normal file
24
09_intuition/frameworks/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
[← Home](../../README.md) · [Intuition](../README.md)
|
||||
|
||||
# GUI Frameworks
|
||||
|
||||
AmigaOS spawned several GUI frameworks beyond the built-in GadTools/BOOPSI system. These frameworks provide layout management, user customization, and richer widget sets — features that the base Intuition API does not offer.
|
||||
|
||||
## Framework Index
|
||||
|
||||
| File | Framework | Era | Description |
|
||||
|---|---|---|---|
|
||||
| [mui/](mui/) | **MUI** (Magic User Interface) | 1993– | De-facto standard. Architecture, developer guide, code examples |
|
||||
| *reaction.md* | **ReAction** / ClassAct | 1997– | Hyperion's official OS 3.5+ GUI toolkit |
|
||||
| *bgui.md* | **BGUI** | 1994– | Lightweight BOOPSI-based layout system |
|
||||
|
||||
## Comparison
|
||||
|
||||
| Feature | MUI | ReAction | BGUI | GadTools |
|
||||
|---|---|---|---|---|
|
||||
| Layout engine | ✅ Constraint-based | ✅ Constraint-based | ✅ Basic | ❌ Manual |
|
||||
| User preferences | ✅ Full | ⚠ Limited | ❌ None | ❌ None |
|
||||
| Custom classes | ✅ MCC ecosystem | ✅ MakeClass | ✅ MakeClass | ❌ N/A |
|
||||
| Availability | Aminet (free runtime) | OS 3.5+ bundled | Aminet (free) | ROM built-in |
|
||||
| Platforms | AmigaOS, MorphOS, AROS | AmigaOS 3.5+ only | AmigaOS 2.0+ | AmigaOS 2.0+ |
|
||||
| Adoption | Very high | Moderate | Low | Universal |
|
||||
133
09_intuition/frameworks/mui/01-introduction.md
Normal file
133
09_intuition/frameworks/mui/01-introduction.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Introduction
|
||||
|
||||
## What is MUI?
|
||||
|
||||
MUI (Magic User Interface) is a system for generating and maintaining graphical user interfaces on Amiga computers. Created by Stefan Stuntz between 1992 and 1997, it provides an object-oriented framework built on top of AmigaOS's native BOOPSI (Basic Object-Oriented Programming System for Intuition) subsystem.
|
||||
|
||||
MUI's defining characteristic is that end users can customize the appearance of any MUI application through a central preferences program. Developers define the structure and behavior; users control the visual style.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Object-oriented design** - Every UI element is an object with attributes and methods
|
||||
- **BOOPSI integration** - Inherits from and extends AmigaOS's native class system
|
||||
- **User customization** - Skinnable via the MUI preferences program without code changes
|
||||
- **Layout management** - Automatic and custom layout systems handle widget positioning
|
||||
- **Notification system** - Declarative event binding between objects
|
||||
- **Comprehensive widget set** - Text, strings, lists, sliders, buttons, gauges, palettes, and more
|
||||
- **Custom class support** - Developers can create reusable components by subclassing existing classes
|
||||
|
||||
## MUI in the Amiga Software Stack
|
||||
|
||||
```
|
||||
+---------------------------+
|
||||
| Your Application |
|
||||
+---------------------------+
|
||||
| MUI |
|
||||
| (classes, layout, events)|
|
||||
+---------------------------+
|
||||
| BOOPSI |
|
||||
| (Intuition classes) |
|
||||
+---------------------------+
|
||||
| Intuition |
|
||||
| (windows, input) |
|
||||
+---------------------------+
|
||||
| Graphics |
|
||||
| (drawing, rastports) |
|
||||
+---------------------------+
|
||||
| Exec |
|
||||
| (tasks, signals) |
|
||||
+---------------------------+
|
||||
```
|
||||
|
||||
### BOOPSI (Basic Object-Oriented Programming System for Intuition)
|
||||
|
||||
BOOPSI is AmigaOS's native object system introduced in AmigaOS 2.0 (V37). It provides the foundational mechanisms that MUI extends:
|
||||
|
||||
- **Class registry** - BOOPSI maintains a system-wide database of classes via `AddClass()` and `FindClass()`
|
||||
- **Object instantiation** - Objects are created with `NewObjectA()` using tag-based attribute lists
|
||||
- **Method dispatch** - Methods are invoked through `DoMethod()`, which routes calls through a class dispatcher
|
||||
- **Inheritance** - Classes specify a superclass and inherit methods and default attributes
|
||||
|
||||
MUI's entire class hierarchy (Notify, Area, Group, Window, Application, etc.) is built as a BOOPSI class tree. Every MUI object is a BOOPSI object. When you call `DoMethod(obj, MUIM_Draw, ...)` you are using BOOPSI dispatch. When MUI creates objects internally, it calls `NewObjectA()` against its own class IDs. The `libraries/mui.h` header defines `MUIC_*` constants that resolve to BOOPSI class names registered with Intuition.
|
||||
|
||||
MUI adds three major capabilities on top of BOOPSI:
|
||||
|
||||
1. **Layout engine** - BOOPSI has no concept of automatic layout; MUI's Group classes compute sizes and positions
|
||||
2. **Notification system** - BOOPSI has no built-in attribute monitoring; MUI's `MUIM_Notify` adds declarative event binding
|
||||
3. **Extended attribute system** - MUI standardizes attribute naming (MUIA_*) and documents I/S/G flags
|
||||
|
||||
### Intuition
|
||||
|
||||
Intuition is AmigaOS's windowing and input subsystem. It is the layer below BOOPSI and the direct interface to the display hardware through the Graphics library.
|
||||
|
||||
MUI's relationship to Intuition:
|
||||
|
||||
- **Windows** - MUI's Window class creates and manages an Intuition window internally. You never call `OpenWindowTagList()` directly; MUI handles it when you set `MUIA_Window_Open` to `TRUE`. The `WindowObject` macro configures Intuition window properties (title, ID, screen) through MUI attributes.
|
||||
- **Input handling** - Intuition captures mouse and keyboard events and routes them through its IDCMP (Intuition Direct Communication Message Port) system. MUI intercepts these events at the Window level and dispatches them to the appropriate widget via `MUIM_HandleEvent` or attribute changes.
|
||||
- **Gadgets** - Intuition provides basic gadget types (boolean, string, proportional). MUI's Gadget subclass wraps Intuition gadgets but replaces their rendering and behavior with MUI's own system. The Boopsi class allows embedding native BOOPSI gadgets inside MUI layouts.
|
||||
- **Screens** - MUI windows open on Intuition screens. You can specify a public screen with `MUIA_Window_Screen` or let MUI use the default Workbench screen.
|
||||
- **RastPorts** - When a custom class renders in `MUIM_Draw`, it draws into an Intuition RastPort obtained via MUI's `_rp(obj)` macro. MUI manages clipping, layer locking, and damage regions.
|
||||
|
||||
Key Intuition structures you encounter when reading MUI code:
|
||||
|
||||
| Structure | Role in MUI |
|
||||
|-----------|-------------|
|
||||
| `struct Window` | Underlying Intuition window managed by MUI Window class |
|
||||
| `struct RastPort` | Drawing context for custom rendering |
|
||||
| `struct DrawInfo` | Pen colors and fonts via `_dri(obj)` |
|
||||
| `struct Screen` | Display surface for windows |
|
||||
| `struct Gadget` | Base structure for Intuition gadgets |
|
||||
|
||||
### GadTools
|
||||
|
||||
GadTools is AmigaOS's higher-level widget library built on top of Intuition gadgets. It provides standardized buttons, checkboxes, sliders, and menus with a consistent look.
|
||||
|
||||
MUI's relationship to GadTools:
|
||||
|
||||
- **Menu compatibility** - MUI can consume GadTools `NewMenu` structures directly via `MUI_MakeObject(MUIO_MenustripNM, newmenu, 0)`. This parses the GadTools menu definition and converts it into MUI's own Menustrip/Menu/Menuitem object tree. This compatibility layer made it easy for existing applications to adopt MUI without rewriting menu code.
|
||||
- **Gadget avoidance** - MUI generally does not use GadTools gadgets for its own widgets. MUI's Area, Text, String, and Button classes are fully independent implementations with their own rendering and input handling. This is why MUI widgets can be skinned through the MUI preferences program while GadTools gadgets have a fixed appearance.
|
||||
- **Requesters** - MUI's Popasl class can invoke GadTools ASL (Amiga Standard Library) file and font requesters as popup subwindows.
|
||||
- **Shared concepts** - Both GadTools and MUI use tag-based construction, so developers familiar with `GTTagList` patterns find MUI's `TAG_DONE` object creation intuitive.
|
||||
|
||||
The official MUI examples demonstrate the GadTools bridge explicitly in `Menus.c`, where a conventional `NewMenu` array is passed to MUI and then manipulated with MUI notifications.
|
||||
|
||||
### Summary of Dependencies
|
||||
|
||||
| Layer | What MUI Uses From It | What MUI Adds |
|
||||
|-------|----------------------|---------------|
|
||||
| **Exec** | Tasks, signals, memory allocation, message ports | Application event loop integration |
|
||||
| **Graphics** | RastPort drawing, pens, regions, fonts | Automatic clipping and damage handling |
|
||||
| **Intuition** | Windows, screens, IDCMP input, layers | Object-oriented window management |
|
||||
| **BOOPSI** | Class registry, `NewObjectA()`, `DoMethodA()` | Layout, notification, extended attributes |
|
||||
| **GadTools** | `NewMenu` structures, ASL requesters | Independent widget rendering and skinning |
|
||||
|
||||
## Version History
|
||||
|
||||
The material in this bootcamp corresponds to **MUI 3.8**, the developer release. Key version details:
|
||||
|
||||
- `muimaster.library` name and version requirements:
|
||||
- Minimum version: **V11** (`MUIMASTER_VMIN`)
|
||||
- Latest version at release: **V19** (`MUIMASTER_VLATEST`)
|
||||
- Some macros in `libraries/mui.h` require V11 or above
|
||||
- The developer package requires AmigaOS 2.0 (v37.175) include files and linker libraries
|
||||
|
||||
## Distribution and Licensing
|
||||
|
||||
MUI was distributed as shareware. The developer package (`mui38dev.lha`) contained autodocs, C examples, includes, and FD files. The user package (`mui38usr.lha`) contained the runtime libraries, preferences program, and demo applications.
|
||||
|
||||
Applications using MUI were encouraged to include a credit file indicating MUI usage.
|
||||
|
||||
## Why MUI Still Matters
|
||||
|
||||
For retrocomputing, emulation, and FPGA platforms like MiSTer, MUI remains relevant because:
|
||||
|
||||
- It represents one of the most sophisticated GUI toolkits available on classic Amiga hardware
|
||||
- Its source code and examples demonstrate mature 1990s C and BOOPSI patterns
|
||||
- Many Amiga applications were built with MUI and understanding it helps with porting and preservation
|
||||
- The architecture influenced later GUI frameworks in its separation of structure from presentation
|
||||
|
||||
---
|
||||
|
||||
Next: [Architecture](02-architecture.md)
|
||||
529
09_intuition/frameworks/mui/02-architecture.md
Normal file
529
09_intuition/frameworks/mui/02-architecture.md
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md) · [MUI](./)
|
||||
|
||||
# Architecture
|
||||
|
||||
This document describes MUI's internal architecture derived from the official MUI 3.8 SDK (`MUIdev.guide`, autodocs, and `libraries/mui.h`).
|
||||
|
||||
---
|
||||
|
||||
## BOOPSI Foundation
|
||||
|
||||
MUI is built on BOOPSI, AmigaOS's Basic Object-Oriented Programming System for Intuition. BOOPSI provides:
|
||||
|
||||
- **Classes** — type definitions containing a dispatcher function and default attributes
|
||||
- **Objects** — instances of classes created with `NewObject()`
|
||||
- **Methods** — operations invoked on objects via `DoMethod()`
|
||||
- **Attributes** — properties set with `SetAttrs()` and read with `GetAttr()`
|
||||
|
||||
MUI extends BOOPSI with its own class hierarchy, layout engine, and notification system while remaining fully compatible with the underlying mechanism. Every MUI object is a valid BOOPSI object; every MUI class is a BOOPSI `IClass`.
|
||||
|
||||
### How MUI Extends BOOPSI
|
||||
|
||||
| BOOPSI Primitive | MUI Extension |
|
||||
|---|---|
|
||||
| `NewObject(class, ...)` | `MUI_NewObject(MUIC_name, ...)` — auto-loads disk-resident classes |
|
||||
| `DoMethod(obj, methodID, ...)` | Same — MUI adds 60+ new method IDs (MUIM_*) |
|
||||
| `SetAttrs(obj, tag, val, ...)` | `set(obj, attr, val)` macro — same mechanism, convenience wrapper |
|
||||
| `GetAttr(attr, obj, &store)` | `get(obj, attr, &store)` macro |
|
||||
| `DisposeObject(obj)` | `MUI_DisposeObject(obj)` — cascading disposal of entire object tree |
|
||||
|
||||
---
|
||||
|
||||
## Class Hierarchy
|
||||
|
||||
The canonical class tree from `libraries/mui.h`:
|
||||
|
||||
```
|
||||
rootclass (BOOPSI's base class)
|
||||
|
|
||||
+-- Notify (implements notification mechanism)
|
||||
| |
|
||||
| +-- Family (handles multiple children)
|
||||
| | |
|
||||
| | +-- Menustrip (describes a complete menu strip)
|
||||
| | +-- Menu (describes a single menu)
|
||||
| | \-- Menuitem (describes a single menu item)
|
||||
| |
|
||||
| +-- Application (main class for all applications)
|
||||
| |
|
||||
| +-- Window (main class for all windows)
|
||||
| | \-- Aboutmui (About window of MUI preferences)
|
||||
| |
|
||||
| +-- Area (base class for all GUI elements)
|
||||
| |
|
||||
| +-- Rectangle (spacing object)
|
||||
| +-- Balance (balancing separator bar)
|
||||
| +-- Image (image display)
|
||||
| +-- Bitmap (draws bitmaps)
|
||||
| | \-- Bodychunk (makes bitmap from ILBM body chunk)
|
||||
| +-- Text (text display)
|
||||
| +-- Gadget (base class for intuition gadgets)
|
||||
| | |
|
||||
| | +-- String (string gadget)
|
||||
| | +-- Boopsi (interface to BOOPSI gadgets)
|
||||
| | \-- Prop (proportional gadget)
|
||||
| |
|
||||
| +-- Gauge (fuel gauge)
|
||||
| +-- Scale (percentage scale)
|
||||
| +-- Colorfield (field with changeable color)
|
||||
| |
|
||||
| +-- List (line-oriented list)
|
||||
| | |
|
||||
| | +-- Floattext (special list with floating text)
|
||||
| | +-- Volumelist (special list with volumes)
|
||||
| | +-- Scrmodelist (special list with screen modes)
|
||||
| | \-- Dirlist (special list with files)
|
||||
| |
|
||||
| +-- Numeric (base class for slider gadgets)
|
||||
| | |
|
||||
| | +-- Knob (turning knob)
|
||||
| | +-- Levelmeter (level display)
|
||||
| | +-- Numericbutton (space saving popup slider)
|
||||
| | \-- Slider (traditional slider)
|
||||
| |
|
||||
| +-- Group (groups other GUI elements)
|
||||
| |
|
||||
| +-- Register (handles page groups with titles)
|
||||
| +-- Virtgroup (handles virtual groups)
|
||||
| +-- Scrollgroup (virtual groups with scrollbars)
|
||||
| +-- Scrollbar (traditional scrollbar)
|
||||
| +-- Listview (listview)
|
||||
| +-- Radio (radio button)
|
||||
| +-- Cycle (cycle gadget)
|
||||
| +-- Coloradjust (several gadgets to adjust a color)
|
||||
| +-- Palette (complete palette gadget)
|
||||
| |
|
||||
| +-- Popstring (base class for popup objects)
|
||||
| |
|
||||
| +-- Popobject (popup anything in a separate window)
|
||||
| | |
|
||||
| | +-- Poplist (popup a simple listview)
|
||||
| | \-- Popscreen (popup a list of public screens)
|
||||
| |
|
||||
| \-- Popasl (popup an asl requester)
|
||||
|
|
||||
+-- Semaphore (semaphore equipped objects)
|
||||
|
|
||||
+-- Applist (private)
|
||||
+-- Dataspace (handles general purpose data spaces)
|
||||
\-- Configdata (private)
|
||||
```
|
||||
|
||||
### Class Role Summary
|
||||
|
||||
| Branch | Root | Responsibility |
|
||||
|---|---|---|
|
||||
| **Notify** | `rootclass → Notify` | Attribute observation, inter-object messaging |
|
||||
| **Application** | `Notify → Application` | Program root, event loop, ARexx, Commodities |
|
||||
| **Window** | `Notify → Window` | Intuition window lifecycle, IDCMP multiplexing |
|
||||
| **Area** | `Notify → Area` | Visual base — sizing, rendering, input, frames |
|
||||
| **Group** | `Area → Group` | Layout container — child arrangement |
|
||||
| **Semaphore** | `Notify → Semaphore` | Thread-safe data containers |
|
||||
|
||||
---
|
||||
|
||||
## Application Object Tree
|
||||
|
||||
A MUI application forms a tree at runtime. Only three class types may have children:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
APP["Application<br/><i>zero or more Window children</i>"]
|
||||
W1["Window 1"]
|
||||
W2["Window 2"]
|
||||
ROOT1["Group<br/><i>root object</i>"]
|
||||
ROOT2["Group<br/><i>root object</i>"]
|
||||
C1["String"]
|
||||
C2["Group"]
|
||||
C3["Text"]
|
||||
C4["List"]
|
||||
C5["Cycle"]
|
||||
|
||||
APP --> W1 & W2
|
||||
W1 --> ROOT1
|
||||
W2 --> ROOT2
|
||||
ROOT1 --> C1 & C2 & C3
|
||||
C2 --> C4 & C5
|
||||
|
||||
style APP fill:#fff3e0,stroke:#ff9800,color:#333
|
||||
style W1 fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
style W2 fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
style ROOT1 fill:#e8f5e9,stroke:#4caf50,color:#333
|
||||
style ROOT2 fill:#e8f5e9,stroke:#4caf50,color:#333
|
||||
```
|
||||
|
||||
| Parent | Allowed Children | Count |
|
||||
|---|---|---|
|
||||
| **Application** | Window objects only | 0..N |
|
||||
| **Window** | Any Area subclass (usually Group) | Exactly 1 |
|
||||
| **Group** | Any Area subclass (including nested Groups) | 1..N |
|
||||
|
||||
Error handling is cascading: if any child creation fails (returns `NULL`), the parent automatically disposes all previously created siblings and returns `NULL` itself. A successful `ApplicationObject` guarantees the entire tree was created. A single `MUI_DisposeObject(app)` destroys everything.
|
||||
|
||||
---
|
||||
|
||||
## Object Lifecycle
|
||||
|
||||
Every MUI object passes through a strict sequence of constructor/destructor pairs. The SDK documents this as the fundamental contract:
|
||||
|
||||
```
|
||||
OM_NEW — parse initial taglist, allocate instance data
|
||||
{
|
||||
MUIM_Setup — display environment known (screen, fonts, DrawInfo)
|
||||
MUIM_AskMinMax — report min/max/default dimensions
|
||||
[ window opens ]
|
||||
{
|
||||
MUIM_Show — object added to window (AddGadget for Intuition types)
|
||||
{
|
||||
MUIM_Draw — render content (may be called multiple times)
|
||||
}
|
||||
MUIM_Hide — object removed from window (RemGadget)
|
||||
}
|
||||
[ window closes ]
|
||||
MUIM_Cleanup — free display-dependent resources
|
||||
}
|
||||
OM_DISPOSE — free all resources, destroy object
|
||||
```
|
||||
|
||||
### Lifecycle as State Machine
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Created: OM_NEW
|
||||
Created --> SetUp: MUIM_Setup
|
||||
SetUp --> Sized: MUIM_AskMinMax
|
||||
Sized --> Visible: MUIM_Show
|
||||
Visible --> Visible: MUIM_Draw (repeated)
|
||||
Visible --> Sized: MUIM_Hide (resize)
|
||||
Sized --> Visible: MUIM_Show (re-layout)
|
||||
Sized --> Created: MUIM_Cleanup
|
||||
Created --> [*]: OM_DISPOSE
|
||||
```
|
||||
|
||||
### Three Levels of Resource Binding
|
||||
|
||||
| Level | Constructor | Destructor | Available Information |
|
||||
|---|---|---|---|
|
||||
| **Object** | `OM_NEW` | `OM_DISPOSE` | Nothing — only taglist parsing |
|
||||
| **Display** | `MUIM_Setup` | `MUIM_Cleanup` | Screen, fonts, DrawInfo, pens |
|
||||
| **Window** | `MUIM_Show` | `MUIM_Hide` | Intuition Window, RastPort, position/size |
|
||||
|
||||
Each level may repeat independently. You may receive multiple Setup/Cleanup cycles (e.g., screen mode change) and multiple Show/Hide cycles within a single Setup (e.g., window resize).
|
||||
|
||||
---
|
||||
|
||||
## Method Dispatch Model
|
||||
|
||||
Every MUI class has a **dispatcher function** — a single entry point that receives all method calls. This is the standard BOOPSI dispatch pattern:
|
||||
|
||||
```c
|
||||
ULONG MyDispatcher(struct IClass *cl, Object *obj, Msg msg)
|
||||
{
|
||||
switch (msg->MethodID)
|
||||
{
|
||||
case OM_NEW : return mNew(cl, obj, (APTR)msg);
|
||||
case OM_DISPOSE : return mDispose(cl, obj, (APTR)msg);
|
||||
case OM_SET : return mSet(cl, obj, (APTR)msg);
|
||||
case OM_GET : return mGet(cl, obj, (APTR)msg);
|
||||
case MUIM_Setup : return mSetup(cl, obj, (APTR)msg);
|
||||
case MUIM_Cleanup : return mCleanup(cl, obj, (APTR)msg);
|
||||
case MUIM_AskMinMax: return mAskMinMax(cl, obj, (APTR)msg);
|
||||
case MUIM_Draw : return mDraw(cl, obj, (APTR)msg);
|
||||
case MUIM_HandleInput: return mHandleInput(cl, obj, (APTR)msg);
|
||||
}
|
||||
|
||||
/* Unknown methods → pass to superclass */
|
||||
return DoSuperMethodA(cl, obj, msg);
|
||||
}
|
||||
```
|
||||
|
||||
### Dispatch Chain
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
CALLER["DoMethod(obj, MUIM_Draw)"] --> DISP_MY["MyClass<br/>Dispatcher"]
|
||||
DISP_MY -->|"handled"| RESULT["Return value"]
|
||||
DISP_MY -->|"DoSuperMethodA()"| DISP_AREA["Area<br/>Dispatcher"]
|
||||
DISP_AREA -->|"DoSuperMethodA()"| DISP_NOTIFY["Notify<br/>Dispatcher"]
|
||||
DISP_NOTIFY -->|"DoSuperMethodA()"| DISP_ROOT["rootclass<br/>Dispatcher"]
|
||||
|
||||
style DISP_MY fill:#e8f5e9,stroke:#4caf50,color:#333
|
||||
style DISP_AREA fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
style DISP_NOTIFY fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
```
|
||||
|
||||
Key rules:
|
||||
- **Always call `DoSuperMethodA()`** for unrecognized methods — MUI sends undocumented internal methods
|
||||
- **Never render in `OM_SET`** — call `MUI_Redraw(obj, flag)` instead; MUI will invoke `MUIM_Draw`
|
||||
- The dispatcher receives `struct IClass *cl` (the class pointer), `Object *obj`, and `Msg msg` in registers `a0`, `a2`, `a1` respectively
|
||||
|
||||
---
|
||||
|
||||
## Notification System
|
||||
|
||||
The central mechanism for controlling a MUI application. Notification creates **declarative reactive bindings** between objects.
|
||||
|
||||
### How It Works
|
||||
|
||||
Every MUI object has attributes that reflect its current state. Attributes change either programmatically (`SetAttrs()`) or through user interaction. Notification watches for these changes and automatically invokes methods on target objects.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Scrollbar as Scrollbar Object
|
||||
participant MUI as MUI Notify Engine
|
||||
participant List as List Object
|
||||
|
||||
User->>Scrollbar: Drags knob
|
||||
Scrollbar->>Scrollbar: MUIA_Prop_First changes
|
||||
Scrollbar->>MUI: Attribute change detected
|
||||
MUI->>MUI: Match against registered notifications
|
||||
MUI->>List: MUIM_Set(MUIA_List_TopPixel, new_value)
|
||||
List->>List: Scrolls to new position
|
||||
```
|
||||
|
||||
### Registration
|
||||
|
||||
```c
|
||||
DoMethod(source, MUIM_Notify,
|
||||
watched_attribute, /* which attribute to observe */
|
||||
trigger_value, /* MUIV_EveryTime or specific value */
|
||||
target_object, /* destination object */
|
||||
param_count, /* number of following parameters */
|
||||
target_method, ...); /* method + arguments to invoke */
|
||||
```
|
||||
|
||||
### Notification Suppression
|
||||
|
||||
When setting attributes programmatically, you may want to suppress notifications to avoid loops:
|
||||
|
||||
```c
|
||||
/* Normal set — triggers any registered notifications */
|
||||
set(slider, MUIA_Slider_Level, 50);
|
||||
|
||||
/* Suppressed set — no notifications fire */
|
||||
SetAttrs(slider, MUIA_NoNotify, TRUE,
|
||||
MUIA_Slider_Level, 50, TAG_DONE);
|
||||
```
|
||||
|
||||
`MUIA_NoNotify` is a one-shot attribute — it only applies to the current `SetAttrs()` call.
|
||||
|
||||
### Deregistration
|
||||
|
||||
```c
|
||||
/* Remove all notifications on a specific attribute */
|
||||
DoMethod(source, MUIM_KillNotify, MUIA_Slider_Level);
|
||||
|
||||
/* Remove notification targeting a specific object */
|
||||
DoMethod(source, MUIM_KillNotifyObj, MUIA_Slider_Level, target);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layout Engine Internals
|
||||
|
||||
MUI's layout engine uses a **three-pass constraint system** that runs before window open and on every resize.
|
||||
|
||||
### Pass 1: AskMinMax (Bottom-Up)
|
||||
|
||||
Every object reports its minimum, maximum, and default dimensions. Leaf objects (Text, String, etc.) report fixed values based on content and font. Group objects aggregate their children:
|
||||
|
||||
**Horizontal Group:**
|
||||
- Min width = sum of all children's min widths
|
||||
- Max width = sum of all children's max widths
|
||||
- Min height = largest child min height
|
||||
- Max height = smallest child max height
|
||||
|
||||
**Vertical Group:**
|
||||
- Min height = sum of all children's min heights
|
||||
- Max height = sum of all children's max heights
|
||||
- Min width = largest child min width
|
||||
- Max width = smallest child max width
|
||||
|
||||
### Pass 2: Size Distribution (Top-Down)
|
||||
|
||||
Starting from the window's current size, the root group distributes available space among children. Extra space (beyond minimum) is distributed proportionally according to **weight** attributes (`MUIA_Weight`, default 100).
|
||||
|
||||
### Pass 3: Placement
|
||||
|
||||
Each object receives its final rectangle `(left, top, width, height)` and records it in instance data accessible via macros:
|
||||
|
||||
| Macro | Returns |
|
||||
|---|---|
|
||||
| `_mleft(obj)` | Left edge of content area (inside frame) |
|
||||
| `_mtop(obj)` | Top edge of content area |
|
||||
| `_mright(obj)` | Right edge of content area |
|
||||
| `_mbottom(obj)` | Bottom edge of content area |
|
||||
| `_mwidth(obj)` | Content area width |
|
||||
| `_mheight(obj)` | Content area height |
|
||||
| `_rp(obj)` | RastPort pointer for rendering |
|
||||
|
||||
### Layout Flow
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Pass 1: AskMinMax (bottom-up)"
|
||||
LEAF1["Text: min=40, max=200"]
|
||||
LEAF2["Button: min=60, max=120"]
|
||||
GROUP1["HGroup: min=100, max=320"]
|
||||
end
|
||||
|
||||
subgraph "Pass 2: Distribute (top-down)"
|
||||
WINDOW["Window size: 250px wide"]
|
||||
CALC["HGroup: 250px available<br/>Text weight=100, Button weight=100<br/>→ Text=125px, Button=125px"]
|
||||
end
|
||||
|
||||
subgraph "Pass 3: Place"
|
||||
PLACE1["Text: left=5, width=125"]
|
||||
PLACE2["Button: left=130, width=125"]
|
||||
end
|
||||
|
||||
LEAF1 & LEAF2 --> GROUP1
|
||||
GROUP1 --> WINDOW --> CALC
|
||||
CALC --> PLACE1 & PLACE2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Handling
|
||||
|
||||
MUI multiplexes Intuition IDCMP messages through the Application object's event loop.
|
||||
|
||||
### Event Flow
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
IDCMP["Intuition IDCMP Port<br/>(mouse, keyboard)"]
|
||||
APP["MUIM_Application_NewInput"]
|
||||
DISPATCH["MUI dispatches to<br/>active/relevant objects"]
|
||||
HANDLE["MUIM_HandleInput<br/>(on each object)"]
|
||||
NOTIFY["Notifications fire<br/>(attribute changes)"]
|
||||
RETURN["Return ID to app loop"]
|
||||
|
||||
IDCMP --> APP --> DISPATCH --> HANDLE --> NOTIFY --> RETURN
|
||||
|
||||
style APP fill:#fff3e0,stroke:#ff9800,color:#333
|
||||
style HANDLE fill:#e8f5e9,stroke:#4caf50,color:#333
|
||||
```
|
||||
|
||||
### IDCMP Subscription
|
||||
|
||||
Custom classes request specific IDCMP events dynamically:
|
||||
|
||||
```c
|
||||
/* During MUIM_HandleInput — request mouse move tracking */
|
||||
MUI_RequestIDCMP(obj, IDCMP_MOUSEMOVE);
|
||||
|
||||
/* Stop tracking when done */
|
||||
MUI_RejectIDCMP(obj, IDCMP_MOUSEMOVE);
|
||||
```
|
||||
|
||||
MUI translates raw IDCMP events into higher-level `MUIKEY_*` constants for keyboard navigation:
|
||||
|
||||
| MUIKEY Constant | Meaning |
|
||||
|---|---|
|
||||
| `MUIKEY_PRESS` | Confirm / activate |
|
||||
| `MUIKEY_TOGGLE` | Toggle selection |
|
||||
| `MUIKEY_UP/DOWN/LEFT/RIGHT` | Directional navigation |
|
||||
| `MUIKEY_PAGEUP/PAGEDOWN` | Page scrolling |
|
||||
| `MUIKEY_TOP/BOTTOM` | Jump to extremes |
|
||||
| `MUIKEY_WORDLEFT/WORDRIGHT` | Word-level navigation |
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Object Linking
|
||||
|
||||
MUI supports **late binding** — adding and removing children after the initial tree is created.
|
||||
|
||||
```c
|
||||
/* Add a window to a running application */
|
||||
DoMethod(app, OM_ADDMEMBER, new_window);
|
||||
set(new_window, MUIA_Window_Open, TRUE);
|
||||
|
||||
/* Remove and dispose when done */
|
||||
set(new_window, MUIA_Window_Open, FALSE);
|
||||
DoMethod(app, OM_REMMEMBER, new_window);
|
||||
MUI_DisposeObject(new_window); /* manual disposal required */
|
||||
```
|
||||
|
||||
For groups, the window must be **closed** before modifying children:
|
||||
|
||||
```c
|
||||
set(window, MUIA_Window_Open, FALSE);
|
||||
DoMethod(group, MUIM_Group_InitChange); /* begin modification */
|
||||
DoMethod(group, OM_ADDMEMBER, new_child);
|
||||
DoMethod(group, MUIM_Group_ExitChange); /* end — triggers re-layout */
|
||||
set(window, MUIA_Window_Open, TRUE);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rendering Model
|
||||
|
||||
MUI uses **retained-mode rendering** — the framework maintains the object tree and handles all damage repair.
|
||||
|
||||
### Draw Flags
|
||||
|
||||
| Flag | Meaning |
|
||||
|---|---|
|
||||
| `MADF_DRAWOBJECT` | Complete redraw of the object |
|
||||
| `MADF_DRAWUPDATE` | Incremental update (optimization) |
|
||||
|
||||
Custom classes call `MUI_Redraw(obj, flag)` to request a redraw. MUI calls `MUIM_Draw` with appropriate flags. Inside `MUIM_Draw`, the class can check which type of redraw is needed:
|
||||
|
||||
```c
|
||||
case MUIM_Draw:
|
||||
{
|
||||
struct MUIP_Draw *msg = (struct MUIP_Draw *)msg;
|
||||
DoSuperMethodA(cl, obj, msg); /* let Area draw background + frame */
|
||||
|
||||
if (msg->flags & MADF_DRAWOBJECT)
|
||||
{
|
||||
/* Full redraw — render everything */
|
||||
}
|
||||
else if (msg->flags & MADF_DRAWUPDATE)
|
||||
{
|
||||
/* Partial update — only changed elements */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Rendering Rules
|
||||
|
||||
1. **Never draw outside `MUIM_Draw`** — not in `OM_SET`, not in `MUIM_HandleInput`
|
||||
2. **Always call `DoSuperMethodA()` first** — Area class draws background and frame
|
||||
3. **Only draw within `_mleft()` / `_mtop()` / `_mright()` / `_mbottom()`** — the content rectangle inside the frame
|
||||
4. **Use `_rp(obj)`** to get the RastPort — never cache it across Show/Hide cycles
|
||||
|
||||
---
|
||||
|
||||
## Tag ID Namespace
|
||||
|
||||
MUI uses the AmigaOS `TagItem` system for all attributes and methods. The namespace is partitioned:
|
||||
|
||||
| Range | Owner |
|
||||
|---|---|
|
||||
| `0x80420000 – 0x8042FFFF` | MUI built-in classes (reserved) |
|
||||
| `TAG_USER \| (serial << 16) \| offset` | Registered third-party classes |
|
||||
|
||||
Each registered MUI developer received a unique serial number. All their class attributes and methods are prefixed with `TAG_USER | (serial << 16)` to avoid tag collisions across the MCC ecosystem.
|
||||
|
||||
---
|
||||
|
||||
## Key Architectural Files
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `libraries/mui.h` | Main header — class names, method IDs, attribute tags, macros, class tree |
|
||||
| `clib/muimaster_protos.h` | C prototypes for `muimaster.library` functions |
|
||||
| `proto/muimaster.h` | Compiler-specific prototype wrappers |
|
||||
| `pragma/muimaster_lib.h` | SAS/C library call pragmas |
|
||||
| `muimaster.library` | Runtime — object creation, layout, event handling, preferences |
|
||||
| `MUI_<Class>.doc` | Per-class autodoc reference (66 files) |
|
||||
| `MUIdev.guide` | Official developer guide — architecture, custom classes, style guide |
|
||||
|
||||
---
|
||||
|
||||
Previous: [Introduction](01-introduction.md)
|
||||
Next: [Getting Started](03-getting-started.md)
|
||||
273
09_intuition/frameworks/mui/03-getting-started.md
Normal file
273
09_intuition/frameworks/mui/03-getting-started.md
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Getting Started
|
||||
|
||||
## Required Includes
|
||||
|
||||
Every MUI application needs at minimum:
|
||||
|
||||
```c
|
||||
#include <libraries/mui.h>
|
||||
```
|
||||
|
||||
Depending on your compiler, you also need one of the following for function prototypes:
|
||||
|
||||
```c
|
||||
/* For SAS/C, DICE, and other traditional compilers */
|
||||
#include <clib/muimaster_protos.h>
|
||||
#include <pragmas/muimaster_pragmas.h>
|
||||
|
||||
/* For GCC */
|
||||
#include <inline/muimaster.h>
|
||||
```
|
||||
|
||||
A typical MUI program also includes standard AmigaOS headers:
|
||||
|
||||
```c
|
||||
#include <dos/dos.h>
|
||||
#include <graphics/gfxmacros.h>
|
||||
#include <workbench/workbench.h>
|
||||
#include <clib/alib_protos.h>
|
||||
#include <clib/exec_protos.h>
|
||||
#include <clib/dos_protos.h>
|
||||
#include <clib/graphics_protos.h>
|
||||
#include <clib/intuition_protos.h>
|
||||
#include <clib/gadtools_protos.h>
|
||||
#include <clib/utility_protos.h>
|
||||
#include <clib/asl_protos.h>
|
||||
```
|
||||
|
||||
## Opening the Library
|
||||
|
||||
Before using any MUI functions, open `muimaster.library`:
|
||||
|
||||
```c
|
||||
#define MUIMASTER_NAME "muimaster.library"
|
||||
#define MUIMASTER_VMIN 11
|
||||
|
||||
struct Library *MUIMasterBase;
|
||||
|
||||
if (!(MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)))
|
||||
{
|
||||
/* handle error */
|
||||
}
|
||||
```
|
||||
|
||||
The minimum required version is **11**. Some macros in `libraries/mui.h` require V11 or above.
|
||||
|
||||
When your application exits, close the library:
|
||||
|
||||
```c
|
||||
CloseLibrary(MUIMasterBase);
|
||||
```
|
||||
|
||||
## Minimal "Hello MUI" Application
|
||||
|
||||
Here is the smallest possible MUI application that opens a window with some text:
|
||||
|
||||
```c
|
||||
#include <libraries/mui.h>
|
||||
#include <clib/muimaster_protos.h>
|
||||
#include <clib/exec_protos.h>
|
||||
#include <clib/intuition_protos.h>
|
||||
#include <pragmas/muimaster_pragmas.h>
|
||||
#include <pragmas/exec_pragmas.h>
|
||||
#include <pragmas/intuition_pragmas.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct Library *MUIMasterBase;
|
||||
extern struct Library *SysBase;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
APTR app, window;
|
||||
|
||||
if (!(MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)))
|
||||
{
|
||||
printf("Failed to open %s\n", MUIMASTER_NAME);
|
||||
return 20;
|
||||
}
|
||||
|
||||
app = ApplicationObject,
|
||||
MUIA_Application_Title , "HelloMUI",
|
||||
MUIA_Application_Version , "$VER: HelloMUI 1.0",
|
||||
MUIA_Application_Copyright , "Your Name",
|
||||
MUIA_Application_Author , "Your Name",
|
||||
MUIA_Application_Description, "A minimal MUI application",
|
||||
MUIA_Application_Base , "HELLOMUI",
|
||||
|
||||
SubWindow, window = WindowObject,
|
||||
MUIA_Window_Title, "Hello MUI",
|
||||
MUIA_Window_ID , MAKE_ID('H','L','O','1'),
|
||||
|
||||
WindowContents, VGroup,
|
||||
Child, TextObject,
|
||||
MUIA_Text_Contents, "\33cHello, MUI World!",
|
||||
End,
|
||||
End,
|
||||
|
||||
End,
|
||||
End;
|
||||
|
||||
if (!app)
|
||||
{
|
||||
printf("Failed to create Application.\n");
|
||||
CloseLibrary(MUIMasterBase);
|
||||
return 20;
|
||||
}
|
||||
|
||||
/* Close window when requested */
|
||||
DoMethod(window, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
|
||||
app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
|
||||
|
||||
set(window, MUIA_Window_Open, TRUE);
|
||||
|
||||
{
|
||||
ULONG sigs = 0;
|
||||
|
||||
while (DoMethod(app, MUIM_Application_NewInput, &sigs)
|
||||
!= MUIV_Application_ReturnID_Quit)
|
||||
{
|
||||
if (sigs)
|
||||
{
|
||||
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
|
||||
if (sigs & SIGBREAKF_CTRL_C)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(window, MUIA_Window_Open, FALSE);
|
||||
MUI_DisposeObject(app);
|
||||
CloseLibrary(MUIMasterBase);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## The demo.h Pattern
|
||||
|
||||
The official MUI examples use a common header file called `demo.h` that centralizes includes and provides helper functions. This is a recommended pattern for your own projects:
|
||||
|
||||
```c
|
||||
/* demo.h - common includes and helpers for MUI applications */
|
||||
|
||||
#include <libraries/mui.h>
|
||||
#include <dos/dos.h>
|
||||
#include <graphics/gfxmacros.h>
|
||||
#include <workbench/workbench.h>
|
||||
#include <clib/alib_protos.h>
|
||||
#include <clib/exec_protos.h>
|
||||
#include <clib/dos_protos.h>
|
||||
#include <clib/icon_protos.h>
|
||||
#include <clib/graphics_protos.h>
|
||||
#include <clib/intuition_protos.h>
|
||||
#include <clib/gadtools_protos.h>
|
||||
#include <clib/utility_protos.h>
|
||||
#include <clib/asl_protos.h>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#include <clib/muimaster_protos.h>
|
||||
#else
|
||||
#include <inline/muimaster.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern struct Library *SysBase, *IntuitionBase, *UtilityBase;
|
||||
extern struct Library *GfxBase, *DOSBase, *IconBase;
|
||||
struct Library *MUIMasterBase;
|
||||
|
||||
/* A fail function that cleans up and exits */
|
||||
static VOID fail(APTR app, char *str)
|
||||
{
|
||||
if (app)
|
||||
MUI_DisposeObject(app);
|
||||
|
||||
#ifndef _DCC
|
||||
if (MUIMasterBase)
|
||||
CloseLibrary(MUIMasterBase);
|
||||
#endif
|
||||
|
||||
if (str)
|
||||
{
|
||||
puts(str);
|
||||
exit(20);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Open muimaster.library */
|
||||
static VOID init(VOID)
|
||||
{
|
||||
#ifndef _DCC
|
||||
if (!(MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)))
|
||||
fail(NULL, "Failed to open " MUIMASTER_NAME ".");
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
## Compiler Setup
|
||||
|
||||
### SAS/C
|
||||
|
||||
The official examples were written for SAS/C. Key settings:
|
||||
|
||||
- Stack size: at least 8192 bytes (`LONG __stack = 8192;`)
|
||||
- Include pragmas for library calls
|
||||
- Use `__saveds` and `__asm` register keywords for dispatcher functions
|
||||
|
||||
### DICE
|
||||
|
||||
DICE requires slightly different macros:
|
||||
|
||||
```c
|
||||
#define REG(x) __ ## x
|
||||
#define ASM
|
||||
#define SAVEDS __geta4
|
||||
```
|
||||
|
||||
DICE also handles library opening differently; the examples use conditional compilation for DICE.
|
||||
|
||||
### GCC
|
||||
|
||||
GCC uses inline headers rather than pragmas:
|
||||
|
||||
```c
|
||||
#include <inline/muimaster.h>
|
||||
```
|
||||
|
||||
GCC does not need `__asm` or `__saveds` for dispatcher functions.
|
||||
|
||||
### MAXON C
|
||||
|
||||
Similar to GCC in that `ASM` and `SAVEDS` are defined as empty:
|
||||
|
||||
```c
|
||||
#define ASM
|
||||
#define SAVEDS
|
||||
```
|
||||
|
||||
## Build Workflow
|
||||
|
||||
A typical build for SAS/C:
|
||||
|
||||
```
|
||||
sc LINK AppWindow.c demo.h
|
||||
```
|
||||
|
||||
For GCC cross-compilation:
|
||||
|
||||
```
|
||||
m68k-amigaos-gcc -o AppWindow AppWindow.c -lmuimaster
|
||||
```
|
||||
|
||||
Ensure your NDK (Native Developer Kit) include paths are set correctly so that `libraries/mui.h` and the clib/inline headers are found.
|
||||
|
||||
---
|
||||
|
||||
Previous: [Architecture](02-architecture.md)
|
||||
Next: [Core Concepts](04-core-concepts.md)
|
||||
235
09_intuition/frameworks/mui/04-core-concepts.md
Normal file
235
09_intuition/frameworks/mui/04-core-concepts.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Core Concepts
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
MUI uses a strict prefix system defined in `libraries/mui.h`. Every identifier type has its own prefix:
|
||||
|
||||
| Prefix | Meaning | Example |
|
||||
|--------|---------|---------|
|
||||
| `MUIC_` | Class name | `MUIC_Area`, `MUIC_Window` |
|
||||
| `MUIM_` | Method | `MUIM_Draw`, `MUIM_AskMinMax` |
|
||||
| `MUIP_` | Method parameter structure | `MUIP_Draw`, `MUIP_AskMinMax` |
|
||||
| `MUIV_` | Special method/attribute value | `MUIV_Application_ReturnID_Quit` |
|
||||
| `MUIA_` | Attribute | `MUIA_Window_Title` |
|
||||
| `MUIE_` | Error return code | `MUIE_OutOfMemory` |
|
||||
| `MUII_` | Standard MUI image | `MUII_BACKGROUND`, `MUII_SHADOW` |
|
||||
| `MUIX_` | Control code for text strings | `MUIX_C` (center), `MUIX_B` (bold) |
|
||||
| `MUIO_` | Object type for `MUI_MakeObject()` | `MUIO_Button`, `MUIO_HBar` |
|
||||
|
||||
Learning these prefixes makes reading MUI code significantly easier.
|
||||
|
||||
## Attributes
|
||||
|
||||
Attributes are the properties of MUI objects. Each attribute definition in `libraries/mui.h` is followed by a comment with the letters **I**, **S**, and/or **G**:
|
||||
|
||||
- **I** - Can be specified at object creation time
|
||||
- **S** - Can be changed later with `SetAttrs()` or the `set()` macro
|
||||
- **G** - Can be read with `GetAttr()` or the `get()` macro
|
||||
|
||||
Example:
|
||||
|
||||
```c
|
||||
#define MUIA_Window_Title 0x8042c21d /* ISG */
|
||||
```
|
||||
|
||||
This means `MUIA_Window_Title` can be set at creation, changed later, and queried at any time.
|
||||
|
||||
### Setting Attributes
|
||||
|
||||
At creation time, attributes are passed as tag-value pairs:
|
||||
|
||||
```c
|
||||
app = ApplicationObject,
|
||||
MUIA_Application_Title, "MyApp",
|
||||
MUIA_Application_Base , "MYAPP",
|
||||
TAG_DONE;
|
||||
```
|
||||
|
||||
After creation, use `set()`:
|
||||
|
||||
```c
|
||||
set(window, MUIA_Window_Open, TRUE);
|
||||
```
|
||||
|
||||
Or use `SetAttrs()` directly:
|
||||
|
||||
```c
|
||||
SetAttrs(window, MUIA_Window_Open, TRUE, TAG_DONE);
|
||||
```
|
||||
|
||||
### Getting Attributes
|
||||
|
||||
```c
|
||||
ULONG is_open;
|
||||
get(window, MUIA_Window_Open, &is_open);
|
||||
```
|
||||
|
||||
## Object Creation Macros
|
||||
|
||||
MUI provides convenience macros that look like function calls but expand into `NewObject()` calls with the appropriate class and tags. These macros make code readable and maintainable.
|
||||
|
||||
### Common Creation Macros
|
||||
|
||||
```c
|
||||
/* Application and Window */
|
||||
ApplicationObject, ... , TAG_DONE;
|
||||
WindowObject, ... , TAG_DONE;
|
||||
|
||||
/* Groups */
|
||||
VGroup, ... , End;
|
||||
HGroup, ... , End;
|
||||
GroupObject, ... , End;
|
||||
|
||||
/* Widgets */
|
||||
TextObject, ... , End;
|
||||
StringObject, ... , End;
|
||||
ListviewObject, ... , End;
|
||||
ListObject, ... , End;
|
||||
SliderObject, ... , End;
|
||||
CycleObject, ... , End;
|
||||
RadioObject, ... , End;
|
||||
GaugeObject, ... , End;
|
||||
ScaleObject, ... , End;
|
||||
ImageObject, ... , End;
|
||||
BitmapObject, ... , End;
|
||||
ColorfieldObject, ... , End;
|
||||
PaletteObject, ... , End;
|
||||
|
||||
/* Popups */
|
||||
PopstringObject, ... , End;
|
||||
PopaslObject, ... , End;
|
||||
PopobjectObject, ... , End;
|
||||
PoplistObject, ... , End;
|
||||
|
||||
/* Special helpers */
|
||||
SimpleButton("Label")
|
||||
HSpace(x)
|
||||
VSpace(x)
|
||||
```
|
||||
|
||||
The pattern is consistent: the macro name corresponds to the class, attributes follow as tag-value pairs, and the object is terminated with `End` or `TAG_DONE`.
|
||||
|
||||
## Tag-Based Construction
|
||||
|
||||
MUI objects are created using the Amiga tag system. Tags are 32-bit values where the upper bits identify the attribute and the lower bits hold the value (or a pointer).
|
||||
|
||||
```c
|
||||
app = ApplicationObject,
|
||||
MUIA_Application_Title , "Demo",
|
||||
MUIA_Application_Version , "$VER: Demo 1.0",
|
||||
MUIA_Application_Copyright , "1997 Author",
|
||||
MUIA_Application_Author , "Author",
|
||||
MUIA_Application_Description, "A demo program",
|
||||
MUIA_Application_Base , "DEMO",
|
||||
SubWindow, window = WindowObject,
|
||||
MUIA_Window_Title, "Demo Window",
|
||||
MUIA_Window_ID , MAKE_ID('D','E','M','O'),
|
||||
WindowContents, VGroup,
|
||||
Child, TextObject,
|
||||
TextFrame,
|
||||
MUIA_Background, MUII_TextBack,
|
||||
MUIA_Text_Contents, "Hello",
|
||||
End,
|
||||
End,
|
||||
End,
|
||||
End;
|
||||
```
|
||||
|
||||
Notice how nesting works: `ApplicationObject` contains `SubWindow`, which contains `WindowObject`, which contains `WindowContents`, which contains a `VGroup`, which contains `Child` widgets.
|
||||
|
||||
## Methods
|
||||
|
||||
Methods are operations you perform on objects. The universal way to invoke a method is `DoMethod()`:
|
||||
|
||||
```c
|
||||
DoMethod(obj, MUIM_MethodName, arg1, arg2, ...);
|
||||
```
|
||||
|
||||
### Common Methods
|
||||
|
||||
| Method | Purpose |
|
||||
|--------|---------|
|
||||
| `MUIM_Application_NewInput` | Process input events (preferred loop) |
|
||||
| `MUIM_Application_Input` | Process input events (legacy loop) |
|
||||
| `MUIM_Application_ReturnID` | Trigger a return ID |
|
||||
| `MUIM_Notify` | Set up attribute notifications |
|
||||
| `MUIM_CallHook` | Invoke a callback hook |
|
||||
| `MUIM_Set` | Set an attribute (method form) |
|
||||
| `MUIM_Get` | Get an attribute (method form) |
|
||||
| `MUIM_Draw` | Render an object |
|
||||
| `MUIM_AskMinMax` | Query minimum/maximum sizes |
|
||||
|
||||
## Object Lifecycle
|
||||
|
||||
### Creation
|
||||
|
||||
Objects are created with the macro constructors. The constructor returns a pointer or `NULL` on failure.
|
||||
|
||||
```c
|
||||
APTR obj = TextObject,
|
||||
MUIA_Text_Contents, "Hello",
|
||||
End;
|
||||
|
||||
if (!obj)
|
||||
/* handle error */;
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
Objects are manipulated through attributes and methods. They exist within a parent container (usually a Group) which manages their layout and visibility.
|
||||
|
||||
### Disposal
|
||||
|
||||
When an object is no longer needed, dispose it:
|
||||
|
||||
```c
|
||||
MUI_DisposeObject(obj);
|
||||
```
|
||||
|
||||
For the top-level Application object, disposing it automatically disposes all child Windows and their contents. This is the normal shutdown pattern:
|
||||
|
||||
```c
|
||||
MUI_DisposeObject(app); /* disposes entire tree */
|
||||
```
|
||||
|
||||
For custom classes, you must also delete the class itself:
|
||||
|
||||
```c
|
||||
MUI_DeleteCustomClass(mcc);
|
||||
```
|
||||
|
||||
## The `Child` Keyword
|
||||
|
||||
When adding widgets to a Group, the `Child` tag is used to introduce each child object:
|
||||
|
||||
```c
|
||||
VGroup,
|
||||
Child, widget1,
|
||||
Child, widget2,
|
||||
Child, HGroup,
|
||||
Child, subwidget1,
|
||||
Child, subwidget2,
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
`Child` is not a macro for a class; it is a tag that tells the Group class to add the following object to its internal list of children.
|
||||
|
||||
## Special Attribute Values
|
||||
|
||||
MUI defines several special values used across multiple attributes:
|
||||
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| `MUIV_EveryTime` | Trigger notification on any attribute change |
|
||||
| `MUIV_TriggerValue` | Pass the triggering value as an argument |
|
||||
| `MUIV_Notification_Value` | Use the value from the notification setup |
|
||||
|
||||
These are used primarily with `MUIM_Notify` to create flexible event bindings.
|
||||
|
||||
---
|
||||
|
||||
Previous: [Getting Started](03-getting-started.md)
|
||||
Next: [Layout System](05-layout-system.md)
|
||||
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)
|
||||
374
09_intuition/frameworks/mui/06-widgets-overview.md
Normal file
374
09_intuition/frameworks/mui/06-widgets-overview.md
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Widgets Overview
|
||||
|
||||
MUI provides a comprehensive set of built-in widget classes. This section provides a quick tour of the most commonly used ones with code snippets.
|
||||
|
||||
## Text
|
||||
|
||||
The Text class displays static text. It supports control codes for formatting.
|
||||
|
||||
```c
|
||||
Child, TextObject,
|
||||
MUIA_Text_Contents, "\33cCentered text",
|
||||
End,
|
||||
```
|
||||
|
||||
### Text Control Codes (MUIX_)
|
||||
|
||||
| Code | Effect |
|
||||
|------|--------|
|
||||
| `\33c` or `MUIX_C` | Center align |
|
||||
| `\33r` or `MUIX_R` | Right align |
|
||||
| `\33l` or `MUIX_L` | Left align |
|
||||
| `\33b` or `MUIX_B` | Bold |
|
||||
| `\33i` or `MUIX_I` | Italic |
|
||||
| `\33u` or `MUIX_U` | Underline |
|
||||
|
||||
Multiple codes can be combined:
|
||||
|
||||
```c
|
||||
"\33c\33bBold centered text"
|
||||
```
|
||||
|
||||
### Common Text Attributes
|
||||
|
||||
| Attribute | Description |
|
||||
|-----------|-------------|
|
||||
| `MUIA_Text_Contents` | The string to display |
|
||||
| `MUIA_Text_HiChar` | Underline this character as shortcut |
|
||||
| `MUIA_Text_PreParse` | Pre-parse string with control codes |
|
||||
| `MUIA_Text_SetVMax` | Set vertical maximum to font height |
|
||||
| `MUIA_Text_SetMin` | Set minimum size to text dimensions |
|
||||
|
||||
## String
|
||||
|
||||
The String class is a single-line text input field.
|
||||
|
||||
```c
|
||||
Child, StringObject,
|
||||
StringFrame,
|
||||
MUIA_String_Contents, "Default text",
|
||||
MUIA_String_MaxLen, 256,
|
||||
MUIA_String_Format, MUIV_String_Format_Left,
|
||||
End,
|
||||
```
|
||||
|
||||
### String Attributes
|
||||
|
||||
| Attribute | Description |
|
||||
|-----------|-------------|
|
||||
| `MUIA_String_Contents` | Current string contents |
|
||||
| `MUIA_String_MaxLen` | Maximum length |
|
||||
| `MUIA_String_Format` | Left, center, or right alignment |
|
||||
| `MUIA_String_Integer` | Parse contents as integer |
|
||||
| `MUIA_String_Accept` | Valid character set |
|
||||
| `MUIA_String_Reject` | Invalid character set |
|
||||
|
||||
## Buttons
|
||||
|
||||
The simplest button uses the `SimpleButton` macro:
|
||||
|
||||
```c
|
||||
Child, SimpleButton("OK"),
|
||||
```
|
||||
|
||||
This is shorthand for:
|
||||
|
||||
```c
|
||||
Child, TextObject,
|
||||
ButtonFrame,
|
||||
MUIA_Background, MUII_ButtonBack,
|
||||
MUIA_Text_Contents, "OK",
|
||||
MUIA_Text_PreParse, "\33c",
|
||||
MUIA_InputMode, MUIV_InputMode_RelVerify,
|
||||
End,
|
||||
```
|
||||
|
||||
## List and Listview
|
||||
|
||||
Lists store lines of data. A Listview wraps a List with scrollbars and input handling.
|
||||
|
||||
### Basic Listview
|
||||
|
||||
```c
|
||||
Child, ListviewObject,
|
||||
MUIA_Listview_Input, FALSE,
|
||||
MUIA_Listview_List, ListObject,
|
||||
ReadListFrame,
|
||||
MUIA_List_ConstructHook, MUIV_List_ConstructHook_String,
|
||||
MUIA_List_DestructHook, MUIV_List_DestructHook_String,
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
### Adding Items
|
||||
|
||||
```c
|
||||
char *entry = "New item";
|
||||
DoMethod(list, MUIM_List_InsertSingle, entry, MUIV_List_Insert_Bottom);
|
||||
```
|
||||
|
||||
### Clearing the List
|
||||
|
||||
```c
|
||||
DoMethod(list, MUIM_List_Clear);
|
||||
```
|
||||
|
||||
### List Attributes
|
||||
|
||||
| Attribute | Description |
|
||||
|-----------|-------------|
|
||||
| `MUIA_List_SourceArray` | Initialize from a NULL-terminated array |
|
||||
| `MUIA_List_ConstructHook` | Hook to construct entries |
|
||||
| `MUIA_List_DestructHook` | Hook to destruct entries |
|
||||
| `MUIA_List_CompareHook` | Hook for sorting |
|
||||
| `MUIA_List_DisplayHook` | Hook for custom rendering |
|
||||
| `MUIA_List_Quiet` | Suppress updates during bulk operations |
|
||||
| `MUIA_List_Active` | Currently selected entry |
|
||||
|
||||
### Specialized List Classes
|
||||
|
||||
| Class | Purpose |
|
||||
|-------|---------|
|
||||
| `Floattext` | Display multi-line text with word wrap |
|
||||
| `Dirlist` | Display directory contents |
|
||||
| `Volumelist` | Display mounted volumes |
|
||||
| `Scrmodelist` | Display available screen modes |
|
||||
|
||||
## Numeric Family
|
||||
|
||||
### Slider
|
||||
|
||||
```c
|
||||
Child, SliderObject,
|
||||
MUIA_Numeric_Min, 0,
|
||||
MUIA_Numeric_Max, 100,
|
||||
MUIA_Numeric_Value, 50,
|
||||
MUIA_Slider_Horiz, TRUE,
|
||||
End,
|
||||
```
|
||||
|
||||
### Knob
|
||||
|
||||
```c
|
||||
Child, KnobObject,
|
||||
MUIA_Numeric_Min, 0,
|
||||
MUIA_Numeric_Max, 255,
|
||||
End,
|
||||
```
|
||||
|
||||
### Levelmeter
|
||||
|
||||
```c
|
||||
Child, LevelmeterObject,
|
||||
MUIA_Numeric_Min, 0,
|
||||
MUIA_Numeric_Max, 100,
|
||||
MUIA_Levelmeter_Label, "Volume",
|
||||
End,
|
||||
```
|
||||
|
||||
### Numericbutton
|
||||
|
||||
A compact popup slider:
|
||||
|
||||
```c
|
||||
Child, NumericbuttonObject,
|
||||
MUIA_Numeric_Min, 0,
|
||||
MUIA_Numeric_Max, 999,
|
||||
End,
|
||||
```
|
||||
|
||||
## Cycle
|
||||
|
||||
A cycle gadget cycles through a set of string labels:
|
||||
|
||||
```c
|
||||
static char *choices[] = { "First", "Second", "Third", NULL };
|
||||
|
||||
Child, CycleObject,
|
||||
MUIA_Cycle_Entries, choices,
|
||||
End,
|
||||
```
|
||||
|
||||
Or using the shorthand macro:
|
||||
|
||||
```c
|
||||
Child, MUI_MakeObject(MUIO_Cycle, NULL, choices),
|
||||
```
|
||||
|
||||
## Radio
|
||||
|
||||
Radio buttons for exclusive selection:
|
||||
|
||||
```c
|
||||
static char *options[] = { "Option A", "Option B", "Option C", NULL };
|
||||
|
||||
Child, RadioObject,
|
||||
MUIA_Radio_Entries, options,
|
||||
End,
|
||||
```
|
||||
|
||||
Or using the shorthand:
|
||||
|
||||
```c
|
||||
Child, MUI_MakeObject(MUIO_Radio, NULL, options),
|
||||
```
|
||||
|
||||
## Gauge
|
||||
|
||||
A horizontal or vertical progress bar:
|
||||
|
||||
```c
|
||||
Child, GaugeObject,
|
||||
MUIA_Gauge_Current, 50,
|
||||
MUIA_Gauge_Max, 100,
|
||||
MUIA_Gauge_Horiz, TRUE,
|
||||
End,
|
||||
```
|
||||
|
||||
## Scale
|
||||
|
||||
A percentage display:
|
||||
|
||||
```c
|
||||
Child, ScaleObject,
|
||||
MUIA_Scale_Horiz, TRUE,
|
||||
End,
|
||||
```
|
||||
|
||||
## Colorfield and Palette
|
||||
|
||||
### Colorfield
|
||||
|
||||
Displays a color that can be changed:
|
||||
|
||||
```c
|
||||
Child, ColorfieldObject,
|
||||
MUIA_Colorfield_RGB, rgb_value,
|
||||
End,
|
||||
```
|
||||
|
||||
### Palette
|
||||
|
||||
A full palette selection gadget:
|
||||
|
||||
```c
|
||||
Child, PaletteObject,
|
||||
MUIA_Palette_Entries, palette_entries,
|
||||
End,
|
||||
```
|
||||
|
||||
## Image and Bitmap
|
||||
|
||||
### Image
|
||||
|
||||
Display a built-in or custom image:
|
||||
|
||||
```c
|
||||
Child, ImageObject,
|
||||
MUIA_Image_Spec, MUII_Close,
|
||||
End,
|
||||
```
|
||||
|
||||
Standard images include `MUII_Close`, `MUII_TapePlay`, `MUII_TapeStop`, `MUII_TapePause`, `MUII_ArrowLeft`, `MUII_ArrowRight`, `MUII_ArrowUp`, `MUII_ArrowDown`, and many others.
|
||||
|
||||
### Bitmap
|
||||
|
||||
Display a custom bitmap:
|
||||
|
||||
```c
|
||||
Child, BitmapObject,
|
||||
MUIA_Bitmap_Width, 32,
|
||||
MUIA_Bitmap_Height, 32,
|
||||
MUIA_Bitmap_Bitmap, myBitMap,
|
||||
MUIA_Bitmap_Transparent, 0,
|
||||
End,
|
||||
```
|
||||
|
||||
### Bodychunk
|
||||
|
||||
Create a bitmap from an ILBM body chunk (useful for embedding images):
|
||||
|
||||
```c
|
||||
Child, BodychunkObject,
|
||||
MUIA_Bitmap_Width, 32,
|
||||
MUIA_Bitmap_Height, 32,
|
||||
MUIA_Bitmap_Bitmap, myBitMap,
|
||||
MUIA_Bodychunk_Body, body_data,
|
||||
MUIA_Bodychunk_Compression, cmpByteRun1,
|
||||
MUIA_Bodychunk_Depth, 5,
|
||||
MUIA_Bodychunk_Masking, mskHasMask,
|
||||
End,
|
||||
```
|
||||
|
||||
## Popup Classes
|
||||
|
||||
### Popstring
|
||||
|
||||
Base class for popup string gadgets. Usually subclassed rather than used directly.
|
||||
|
||||
### Popasl
|
||||
|
||||
Popup an ASL file or font requester:
|
||||
|
||||
```c
|
||||
Child, PopaslObject,
|
||||
MUIA_Popstring_String, StringObject, StringFrame, End,
|
||||
MUIA_Popasl_Type, ASL_FileRequest,
|
||||
MUIA_Popasl_StartHook, &startHook,
|
||||
End,
|
||||
```
|
||||
|
||||
### Poplist
|
||||
|
||||
Popup a simple list:
|
||||
|
||||
```c
|
||||
Child, PoplistObject,
|
||||
MUIA_Popstring_String, StringObject, StringFrame, End,
|
||||
MUIA_Poplist_Array, string_array,
|
||||
End,
|
||||
```
|
||||
|
||||
### Popobject
|
||||
|
||||
Popup any arbitrary object tree:
|
||||
|
||||
```c
|
||||
Child, PopobjectObject,
|
||||
MUIA_Popstring_String, StringObject, StringFrame, End,
|
||||
MUIA_Popobject_Object, ListviewObject,
|
||||
MUIA_Listview_List, customList,
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
## Register
|
||||
|
||||
A tabbed group where each page is a separate group:
|
||||
|
||||
```c
|
||||
Child, RegisterGroup(labels),
|
||||
Child, VGroup,
|
||||
/* Page 1 contents */
|
||||
End,
|
||||
Child, VGroup,
|
||||
/* Page 2 contents */
|
||||
End,
|
||||
Child, VGroup,
|
||||
/* Page 3 contents */
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
Where `labels` is a NULL-terminated array of tab titles:
|
||||
|
||||
```c
|
||||
static char *labels[] = { "General", "Advanced", "About", NULL };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Previous: [Layout System](05-layout-system.md)
|
||||
Next: [Windows and Applications](07-windows-and-applications.md)
|
||||
213
09_intuition/frameworks/mui/07-windows-and-applications.md
Normal file
213
09_intuition/frameworks/mui/07-windows-and-applications.md
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Windows and Applications
|
||||
|
||||
## The Application Object
|
||||
|
||||
Every MUI program needs exactly one Application object. It is the root of the object tree and manages:
|
||||
|
||||
- All windows (via `SubWindow`)
|
||||
- The program's identity (title, version, author)
|
||||
- The main input loop
|
||||
- Iconification and AppMessage handling
|
||||
- Return ID dispatching
|
||||
|
||||
### Creating an Application
|
||||
|
||||
```c
|
||||
app = ApplicationObject,
|
||||
MUIA_Application_Title , "MyApp",
|
||||
MUIA_Application_Version , "$VER: MyApp 1.0 (01.01.97)",
|
||||
MUIA_Application_Copyright , "1997 Author Name",
|
||||
MUIA_Application_Author , "Author Name",
|
||||
MUIA_Application_Description, "Description of what this does",
|
||||
MUIA_Application_Base , "MYAPP",
|
||||
|
||||
SubWindow, window = WindowObject,
|
||||
MUIA_Window_Title, "Main Window",
|
||||
MUIA_Window_ID , MAKE_ID('M','A','I','N'),
|
||||
WindowContents, VGroup,
|
||||
/* widgets */
|
||||
End,
|
||||
End,
|
||||
|
||||
End;
|
||||
```
|
||||
|
||||
### Application Attributes
|
||||
|
||||
| Attribute | Description |
|
||||
|-----------|-------------|
|
||||
| `MUIA_Application_Title` | Program title shown in system exchanges |
|
||||
| `MUIA_Application_Version` | Version string, conventionally `$VER: Name Version (Date)` |
|
||||
| `MUIA_Application_Copyright` | Copyright notice |
|
||||
| `MUIA_Application_Author` | Author name |
|
||||
| `MUIA_Application_Description` | Short description |
|
||||
| `MUIA_Application_Base` | Base name for disk object, ARexx port, etc. |
|
||||
| `MUIA_Application_Window` | Alternative to `SubWindow` for adding windows |
|
||||
| `MUIA_Application_DropObject` | Object to receive icons dropped on app icon |
|
||||
|
||||
## The Window Object
|
||||
|
||||
Windows are children of the Application. Each Window contains a single root widget specified with `WindowContents`.
|
||||
|
||||
### Window Attributes
|
||||
|
||||
| Attribute | Description |
|
||||
|-----------|-------------|
|
||||
| `MUIA_Window_Title` | Window title bar text |
|
||||
| `MUIA_Window_ID` | Four-byte identifier for state persistence |
|
||||
| `MUIA_Window_Open` | Open or close the window (ISG) |
|
||||
| `MUIA_Window_Screen` | Public screen to open on |
|
||||
| `MUIA_Window_Menustrip` | Menu strip for this window |
|
||||
| `MUIA_Window_AppWindow` | Accept Workbench icons (TRUE/FALSE) |
|
||||
| `MUIA_Window_NoMenus` | Disable menus for this window |
|
||||
| `MUIA_Window_ActiveObject` | Object to receive initial activation |
|
||||
| `MUIA_Window_CloseRequest` | Set when user clicks close gadget (triggers notification) |
|
||||
|
||||
### Opening and Closing
|
||||
|
||||
```c
|
||||
/* Open the window */
|
||||
set(window, MUIA_Window_Open, TRUE);
|
||||
|
||||
/* Later, close it */
|
||||
set(window, MUIA_Window_Open, FALSE);
|
||||
```
|
||||
|
||||
Always close windows before disposing the Application.
|
||||
|
||||
## SubWindow vs Standalone Windows
|
||||
|
||||
The `SubWindow` tag inside `ApplicationObject` both creates the window and registers it with the application in one step. This is the recommended pattern:
|
||||
|
||||
```c
|
||||
app = ApplicationObject,
|
||||
...
|
||||
SubWindow, window = WindowObject, ... , End,
|
||||
SubWindow, window2 = WindowObject, ... , End,
|
||||
End;
|
||||
```
|
||||
|
||||
Alternatively, you can create windows separately and add them later with `MUIA_Application_Window`, but `SubWindow` is more common in examples.
|
||||
|
||||
## Multiple Windows
|
||||
|
||||
An application can have multiple windows. Each window can be opened and closed independently:
|
||||
|
||||
```c
|
||||
app = ApplicationObject,
|
||||
...
|
||||
SubWindow, mainWin = WindowObject,
|
||||
MUIA_Window_Title, "Main",
|
||||
MUIA_Window_ID , MAKE_ID('M','A','I','N'),
|
||||
...
|
||||
End,
|
||||
|
||||
SubWindow, helpWin = WindowObject,
|
||||
MUIA_Window_Title, "Help",
|
||||
MUIA_Window_ID , MAKE_ID('H','E','L','P'),
|
||||
...
|
||||
End,
|
||||
End;
|
||||
```
|
||||
|
||||
Each window should have a unique `MUIA_Window_ID` so MUI can save and restore its position and size.
|
||||
|
||||
## AppWindows
|
||||
|
||||
An AppWindow accepts icons dragged from Workbench. Enable it with `MUIA_Window_AppWindow`:
|
||||
|
||||
```c
|
||||
WindowObject,
|
||||
MUIA_Window_Title , "Drop icons on me!",
|
||||
MUIA_Window_ID , MAKE_ID('A','P','P','W'),
|
||||
MUIA_Window_AppWindow, TRUE,
|
||||
...
|
||||
End,
|
||||
```
|
||||
|
||||
When an icon is dropped, the object that received it gets an `AppMessage`. You can handle this with a notification:
|
||||
|
||||
```c
|
||||
SAVEDS ASM LONG AppMsgFunc(REG(a2) APTR obj, REG(a1) struct AppMessage **x)
|
||||
{
|
||||
struct AppMessage *amsg = *x;
|
||||
struct WBArg *ap;
|
||||
int i;
|
||||
static char buf[256];
|
||||
|
||||
for (ap = amsg->am_ArgList, i = 0; i < amsg->am_NumArgs; i++, ap++)
|
||||
{
|
||||
NameFromLock(ap->wa_Lock, buf, sizeof(buf));
|
||||
AddPart(buf, ap->wa_Name, sizeof(buf));
|
||||
DoMethod(obj, MUIM_List_Insert, &buf, 1, MUIV_List_Insert_Bottom);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct Hook AppMsgHook = {
|
||||
{ NULL, NULL }, (VOID *)AppMsgFunc, NULL, NULL
|
||||
};
|
||||
|
||||
/* In setup: */
|
||||
DoMethod(lv1, MUIM_Notify, MUIA_AppMessage, MUIV_EveryTime,
|
||||
lv1, 3, MUIM_CallHook, &AppMsgHook, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
### DropObject
|
||||
|
||||
When the application is iconified, icons dropped on its AppIcon can be routed to a specific object:
|
||||
|
||||
```c
|
||||
set(app, MUIA_Application_DropObject, targetList);
|
||||
```
|
||||
|
||||
## Iconification
|
||||
|
||||
MUI handles iconification automatically. When the user clicks the zoom gadget (or uses the menu item), the application window closes and an AppIcon appears on Workbench. Pressing the AppIcon restores the window.
|
||||
|
||||
You can react to iconification state changes via notifications on `MUIA_Application_Iconified`.
|
||||
|
||||
## Window State Persistence
|
||||
|
||||
MUI automatically saves window positions and sizes using the `MUIA_Window_ID`. The ID is a four-byte value conventionally created with `MAKE_ID`:
|
||||
|
||||
```c
|
||||
#define MAKE_ID(a,b,c,d) ((ULONG)(a)<<24 | (ULONG)(b)<<16 | (ULONG)(c)<<8 | (ULONG)(d))
|
||||
|
||||
MUIA_Window_ID, MAKE_ID('M','A','I','N'),
|
||||
```
|
||||
|
||||
Each window in your application should have a unique ID. MUI stores positions in the `ENV:` directory.
|
||||
|
||||
## Common Window Patterns
|
||||
|
||||
### Centered Dialog
|
||||
|
||||
```c
|
||||
WindowObject,
|
||||
MUIA_Window_Title, "Confirm",
|
||||
MUIA_Window_ID , MAKE_ID('C','N','F','M'),
|
||||
MUIA_Window_Width, MUIV_Window_Width_MinMax(20),
|
||||
WindowContents, VGroup,
|
||||
Child, TextObject,
|
||||
TextFrame,
|
||||
MUIA_Text_Contents, "\33cAre you sure?",
|
||||
End,
|
||||
Child, HGroup,
|
||||
Child, SimpleButton("Yes"),
|
||||
Child, SimpleButton("No"),
|
||||
End,
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
### Modal Window
|
||||
|
||||
MUI does not have a built-in modal window concept, but you can simulate it by disabling input on the main window and running a sub-loop for the dialog window.
|
||||
|
||||
---
|
||||
|
||||
Previous: [Widgets Overview](06-widgets-overview.md)
|
||||
Next: [Menus](08-menus.md)
|
||||
211
09_intuition/frameworks/mui/08-menus.md
Normal file
211
09_intuition/frameworks/mui/08-menus.md
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Menus
|
||||
|
||||
## Menu System Architecture
|
||||
|
||||
MUI's menu system consists of three class types:
|
||||
|
||||
| Class | Role |
|
||||
|-------|------|
|
||||
| `Menustrip` | Root container holding all menus |
|
||||
| `Menu` | A single pull-down menu (e.g., "Project") |
|
||||
| `Menuitem` | An individual item within a menu |
|
||||
|
||||
A Menustrip is attached to either a specific Window (via `MUIA_Window_Menustrip`) or to the Application (via `MUIA_Application_Menustrip`). When attached to the Application, all windows inherit the menu unless overridden.
|
||||
|
||||
## Using GadTools NewMenu
|
||||
|
||||
The easiest way to define menus is with the familiar GadTools `NewMenu` structure. MUI converts this into its own object tree automatically.
|
||||
|
||||
### Define the Menu Structure
|
||||
|
||||
```c
|
||||
enum {
|
||||
MEN_PROJECT = 1,
|
||||
MEN_ABOUT,
|
||||
MEN_QUIT,
|
||||
MEN_EDIT,
|
||||
MEN_CUT,
|
||||
MEN_COPY,
|
||||
MEN_PASTE
|
||||
};
|
||||
|
||||
static struct NewMenu MenuData[] = {
|
||||
{ NM_TITLE, "Project" , 0, 0, 0, (APTR)MEN_PROJECT },
|
||||
{ NM_ITEM , "About...", "?", 0, 0, (APTR)MEN_ABOUT },
|
||||
{ NM_ITEM , NM_BARLABEL, 0, 0, 0, (APTR)0 },
|
||||
{ NM_ITEM , "Quit" , "Q", 0, 0, (APTR)MEN_QUIT },
|
||||
|
||||
{ NM_TITLE, "Edit" , 0, 0, 0, (APTR)MEN_EDIT },
|
||||
{ NM_ITEM , "Cut" , "X", 0, 0, (APTR)MEN_CUT },
|
||||
{ NM_ITEM , "Copy" , "C", 0, 0, (APTR)MEN_COPY },
|
||||
{ NM_ITEM , "Paste" , "V", 0, 0, (APTR)MEN_PASTE },
|
||||
|
||||
{ NM_END, NULL, 0, 0, 0, (APTR)0 },
|
||||
};
|
||||
```
|
||||
|
||||
### Create and Attach the Menu
|
||||
|
||||
```c
|
||||
app = ApplicationObject,
|
||||
...
|
||||
SubWindow, win = WindowObject,
|
||||
MUIA_Window_Title , "Menus",
|
||||
MUIA_Window_ID , MAKE_ID('M','E','N','1'),
|
||||
MUIA_Window_Menustrip, strip = MUI_MakeObject(MUIO_MenustripNM, MenuData, 0),
|
||||
WindowContents, VGroup,
|
||||
...
|
||||
End,
|
||||
End,
|
||||
End;
|
||||
```
|
||||
|
||||
`MUI_MakeObject(MUIO_MenustripNM, MenuData, 0)` parses the `NewMenu` array and returns a Menustrip object.
|
||||
|
||||
## Handling Menu Selections
|
||||
|
||||
### Method 1: Return IDs in the Input Loop
|
||||
|
||||
Each menu item's `UserData` becomes its Return ID. In the main loop, check for it:
|
||||
|
||||
```c
|
||||
while (running)
|
||||
{
|
||||
switch (DoMethod(app, MUIM_Application_Input, &signals))
|
||||
{
|
||||
case MUIV_Application_ReturnID_Quit:
|
||||
running = FALSE;
|
||||
break;
|
||||
|
||||
case MEN_ABOUT:
|
||||
/* show about box */
|
||||
break;
|
||||
|
||||
case MEN_CUT:
|
||||
/* perform cut */
|
||||
break;
|
||||
}
|
||||
|
||||
if (running && signals)
|
||||
Wait(signals);
|
||||
}
|
||||
```
|
||||
|
||||
### Method 2: Notifications
|
||||
|
||||
Bind menu items directly to actions using `MUIM_Notify`:
|
||||
|
||||
```c
|
||||
APTR aboutItem;
|
||||
|
||||
/* Find the item using its userdata */
|
||||
aboutItem = (APTR)DoMethod(strip, MUIM_FindUserData, MEN_ABOUT);
|
||||
|
||||
if (aboutItem)
|
||||
{
|
||||
DoMethod(aboutItem, MUIM_Notify, MUIA_Menuitem_Trigger, MUIV_EveryTime,
|
||||
app, 2, MUIM_Application_ReturnID, MEN_ABOUT);
|
||||
}
|
||||
```
|
||||
|
||||
### Method 3: Hooks
|
||||
|
||||
For more complex actions, use `MUIM_CallHook`:
|
||||
|
||||
```c
|
||||
SAVEDS ASM LONG AboutFunc(REG(a2) APTR obj, REG(a1) APTR msg)
|
||||
{
|
||||
MUI_Request(app, win, 0, "About", "*OK",
|
||||
"MyApp\nVersion 1.0\nBy Author");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct Hook AboutHook = { {0,0}, (VOID *)AboutFunc, NULL, NULL };
|
||||
|
||||
/* In setup: */
|
||||
DoMethod(aboutItem, MUIM_Notify, MUIA_Menuitem_Trigger, MUIV_EveryTime,
|
||||
app, 3, MUIM_CallHook, &AboutHook, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
## Dynamic Menu Manipulation
|
||||
|
||||
### Enabling and Disabling Items
|
||||
|
||||
```c
|
||||
/* Disable a menu item */
|
||||
set(menuItem, MUIA_Menuitem_Enabled, FALSE);
|
||||
|
||||
/* Enable it again */
|
||||
set(menuItem, MUIA_Menuitem_Enabled, TRUE);
|
||||
```
|
||||
|
||||
### Checkmarks and Radio Groups
|
||||
|
||||
Use standard GadTools flags in the `NewMenu` structure:
|
||||
|
||||
```c
|
||||
#define RB CHECKIT
|
||||
|
||||
static struct NewMenu MenuData[] = {
|
||||
{ NM_TITLE, "Settings" , 0 ,0 ,0,(APTR)MEN_SETTINGS },
|
||||
{ NM_ITEM , "Hardware" , 0 ,NM_ITEMDISABLED,0,(APTR)MEN_HARDWARE },
|
||||
{ NM_SUB , "A1000" ,"1",RB|CHECKED,2|4|8 ,(APTR)MEN_A1000 },
|
||||
{ NM_SUB , "A2000" ,"2",RB ,1|4|8 ,(APTR)MEN_A2000 },
|
||||
{ NM_SUB , "A3000" ,"3",RB ,1|2|8 ,(APTR)MEN_A3000 },
|
||||
{ NM_SUB , "A4000" ,"4",RB ,1|2|4 ,(APTR)MEN_A4000 },
|
||||
{ NM_END , NULL , 0 ,0 ,0,(APTR)0 },
|
||||
};
|
||||
```
|
||||
|
||||
`CHECKIT` makes the item checkable. `CHECKED` pre-checks it. The mutual exclude field (e.g., `2|4|8`) defines radio groups: items with overlapping bits cannot be checked simultaneously.
|
||||
|
||||
### Toggle Items
|
||||
|
||||
Use `MENUTOGGLE` for items that toggle on/off independently:
|
||||
|
||||
```c
|
||||
#define TG CHECKIT|MENUTOGGLE
|
||||
|
||||
{ NM_SUB, "Option", "O", TG, 0, (APTR)MEN_OPTION },
|
||||
```
|
||||
|
||||
## Creating Menus Programmatically
|
||||
|
||||
Instead of `NewMenu`, you can build the menu tree manually:
|
||||
|
||||
```c
|
||||
MUIA_Window_Menustrip, MenustripObject,
|
||||
Child, MenuObject,
|
||||
MUIA_Menu_Title, "Project",
|
||||
Child, MenuitemObject,
|
||||
MUIA_Menuitem_Title, "About...",
|
||||
MUIA_Menuitem_Shortcut, "?",
|
||||
End,
|
||||
Child, MenuitemObject,
|
||||
MUIA_Menuitem_Title, NM_BARLABEL,
|
||||
End,
|
||||
Child, MenuitemObject,
|
||||
MUIA_Menuitem_Title, "Quit",
|
||||
MUIA_Menuitem_Shortcut, "Q",
|
||||
End,
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
This is more verbose but gives you direct access to every menu item object for notifications.
|
||||
|
||||
## Menu Best Practices
|
||||
|
||||
- Always provide keyboard shortcuts for common actions
|
||||
- Use `NM_BARLABEL` to separate logical groups
|
||||
- Keep the `NewMenu` userdata values unique across the entire menu
|
||||
- Attach menus to the Application if all windows share the same menu
|
||||
- Attach menus to individual Windows only when menus differ per window
|
||||
- Use `MUIM_FindUserData` on the Menustrip to locate individual items for notifications
|
||||
|
||||
---
|
||||
|
||||
Previous: [Windows and Applications](07-windows-and-applications.md)
|
||||
Next: [Custom Classes](09-custom-classes.md)
|
||||
292
09_intuition/frameworks/mui/09-custom-classes.md
Normal file
292
09_intuition/frameworks/mui/09-custom-classes.md
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Custom Classes
|
||||
|
||||
## Why Create Custom Classes?
|
||||
|
||||
Custom classes let you encapsulate reusable behavior and rendering. Instead of writing the same notification setup and hook logic repeatedly, you create a class that handles everything internally. Examples include:
|
||||
|
||||
- A custom chart or graph widget
|
||||
- A specialized list with drag-and-drop rules
|
||||
- A rendering area with custom graphics
|
||||
- A compound widget that bundles multiple controls
|
||||
|
||||
Custom classes are standard BOOPSI classes with MUI-specific methods.
|
||||
|
||||
## Creating a Custom Class
|
||||
|
||||
### Step 1: Define Instance Data
|
||||
|
||||
Instance data is a structure that holds per-object state. MUI allocates it for each object automatically.
|
||||
|
||||
```c
|
||||
struct MyData
|
||||
{
|
||||
LONG dummy;
|
||||
/* add your own fields here */
|
||||
};
|
||||
```
|
||||
|
||||
### Step 2: Implement Methods
|
||||
|
||||
At minimum, most visual custom classes implement two methods:
|
||||
|
||||
- `MUIM_AskMinMax` - Report size requirements
|
||||
- `MUIM_Draw` - Render the object
|
||||
|
||||
#### AskMinMax
|
||||
|
||||
Called before layout. You must add your size requirements to what the superclass already calculated.
|
||||
|
||||
```c
|
||||
SAVEDS ULONG mAskMinMax(struct IClass *cl, Object *obj, struct MUIP_AskMinMax *msg)
|
||||
{
|
||||
/* Let superclass fill in base values (frame, spacing, etc.) */
|
||||
DoSuperMethodA(cl, obj, msg);
|
||||
|
||||
/* Add our own requirements */
|
||||
msg->MinMaxInfo->MinWidth += 100;
|
||||
msg->MinMaxInfo->DefWidth += 120;
|
||||
msg->MinMaxInfo->MaxWidth += 500;
|
||||
|
||||
msg->MinMaxInfo->MinHeight += 40;
|
||||
msg->MinMaxInfo->DefHeight += 90;
|
||||
msg->MinMaxInfo->MaxHeight += 300;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Important: Use `+=` to add to the superclass values, not `=` to replace them.
|
||||
|
||||
#### Draw
|
||||
|
||||
Called whenever MUI needs you to render.
|
||||
|
||||
```c
|
||||
SAVEDS ULONG mDraw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Let superclass draw frame, background, etc. */
|
||||
DoSuperMethodA(cl, obj, msg);
|
||||
|
||||
/* If MADF_DRAWOBJECT isn't set, don't draw content */
|
||||
if (!(msg->flags & MADF_DRAWOBJECT))
|
||||
return 0;
|
||||
|
||||
/* Render within the object's rectangle */
|
||||
SetAPen(_rp(obj), _dri(obj)->dri_Pens[TEXTPEN]);
|
||||
|
||||
for (i = _mleft(obj); i <= _mright(obj); i += 5)
|
||||
{
|
||||
Move(_rp(obj), _mleft(obj), _mbottom(obj));
|
||||
Draw(_rp(obj), i, _mtop(obj));
|
||||
Move(_rp(obj), _mright(obj), _mbottom(obj));
|
||||
Draw(_rp(obj), i, _mtop(obj));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Key macros for rendering:
|
||||
|
||||
| Macro | Meaning |
|
||||
|-------|---------|
|
||||
| `_rp(obj)` | RastPort for drawing |
|
||||
| `_dri(obj)` | DrawInfo (pens, fonts) |
|
||||
| `_mleft(obj)` | Left edge of render area |
|
||||
| `_mtop(obj)` | Top edge of render area |
|
||||
| `_mright(obj)` | Right edge of render area |
|
||||
| `_mbottom(obj)` | Bottom edge of render area |
|
||||
| `_mwidth(obj)` | Width of render area |
|
||||
| `_mheight(obj)` | Height of render area |
|
||||
|
||||
### Step 3: Create the Dispatcher
|
||||
|
||||
The dispatcher is a switch statement that routes methods to your handlers.
|
||||
|
||||
```c
|
||||
SAVEDS ASM ULONG MyDispatcher(REG(a0) struct IClass *cl,
|
||||
REG(a2) Object *obj,
|
||||
REG(a1) Msg msg)
|
||||
{
|
||||
switch (msg->MethodID)
|
||||
{
|
||||
case MUIM_AskMinMax: return mAskMinMax(cl, obj, (APTR)msg);
|
||||
case MUIM_Draw: return mDraw(cl, obj, (APTR)msg);
|
||||
}
|
||||
|
||||
return DoSuperMethodA(cl, obj, msg);
|
||||
}
|
||||
```
|
||||
|
||||
Unknown methods are passed to the superclass via `DoSuperMethodA()`.
|
||||
|
||||
### Step 4: Register the Class
|
||||
|
||||
```c
|
||||
struct MUI_CustomClass *mcc;
|
||||
|
||||
mcc = MUI_CreateCustomClass(NULL, /* library base (usually NULL) */
|
||||
MUIC_Area, /* superclass */
|
||||
NULL, /* private data (usually NULL) */
|
||||
sizeof(struct MyData),
|
||||
MyDispatcher);
|
||||
|
||||
if (!mcc)
|
||||
fail(NULL, "Could not create custom class.");
|
||||
```
|
||||
|
||||
`MUI_CreateCustomClass` returns a `struct MUI_CustomClass *`, not a raw `struct IClass *`. The `mcc_Class` member contains the actual class pointer for `NewObject()`.
|
||||
|
||||
### Step 5: Use the Class
|
||||
|
||||
```c
|
||||
MyObj = NewObject(mcc->mcc_Class, NULL,
|
||||
TextFrame,
|
||||
MUIA_Background, MUII_BACKGROUND,
|
||||
TAG_DONE);
|
||||
```
|
||||
|
||||
### Step 6: Cleanup
|
||||
|
||||
When your application exits, delete the custom class:
|
||||
|
||||
```c
|
||||
MUI_DeleteCustomClass(mcc);
|
||||
```
|
||||
|
||||
Do this after disposing all objects of that class.
|
||||
|
||||
## Complete Minimal Custom Class Example
|
||||
|
||||
```c
|
||||
#include "demo.h"
|
||||
|
||||
struct MyData
|
||||
{
|
||||
LONG dummy;
|
||||
};
|
||||
|
||||
SAVEDS ULONG mAskMinMax(struct IClass *cl, Object *obj, struct MUIP_AskMinMax *msg)
|
||||
{
|
||||
DoSuperMethodA(cl, obj, msg);
|
||||
|
||||
msg->MinMaxInfo->MinWidth += 100;
|
||||
msg->MinMaxInfo->DefWidth += 120;
|
||||
msg->MinMaxInfo->MaxWidth += 500;
|
||||
msg->MinMaxInfo->MinHeight += 40;
|
||||
msg->MinMaxInfo->DefHeight += 90;
|
||||
msg->MinMaxInfo->MaxHeight += 300;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SAVEDS ULONG mDraw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
|
||||
{
|
||||
DoSuperMethodA(cl, obj, msg);
|
||||
|
||||
if (!(msg->flags & MADF_DRAWOBJECT))
|
||||
return 0;
|
||||
|
||||
SetAPen(_rp(obj), _dri(obj)->dri_Pens[TEXTPEN]);
|
||||
RectFill(_rp(obj), _mleft(obj), _mtop(obj), _mright(obj), _mbottom(obj));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SAVEDS ASM ULONG MyDispatcher(REG(a0) struct IClass *cl,
|
||||
REG(a2) Object *obj,
|
||||
REG(a1) Msg msg)
|
||||
{
|
||||
switch (msg->MethodID)
|
||||
{
|
||||
case MUIM_AskMinMax: return mAskMinMax(cl, obj, (APTR)msg);
|
||||
case MUIM_Draw: return mDraw(cl, obj, (APTR)msg);
|
||||
}
|
||||
return DoSuperMethodA(cl, obj, msg);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
APTR app, window, MyObj;
|
||||
struct MUI_CustomClass *mcc;
|
||||
|
||||
init();
|
||||
|
||||
mcc = MUI_CreateCustomClass(NULL, MUIC_Area, NULL,
|
||||
sizeof(struct MyData), MyDispatcher);
|
||||
if (!mcc)
|
||||
fail(NULL, "Could not create custom class.");
|
||||
|
||||
app = ApplicationObject,
|
||||
MUIA_Application_Title, "CustomClassDemo",
|
||||
...
|
||||
SubWindow, window = WindowObject,
|
||||
...
|
||||
WindowContents, VGroup,
|
||||
Child, MyObj = NewObject(mcc->mcc_Class, NULL,
|
||||
TextFrame,
|
||||
MUIA_Background, MUII_BACKGROUND,
|
||||
TAG_DONE),
|
||||
End,
|
||||
End,
|
||||
End;
|
||||
|
||||
if (!app)
|
||||
fail(app, "Failed to create Application.");
|
||||
|
||||
DoMethod(window, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
|
||||
app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
|
||||
|
||||
set(window, MUIA_Window_Open, TRUE);
|
||||
|
||||
{
|
||||
ULONG sigs = 0;
|
||||
while (DoMethod(app, MUIM_Application_NewInput, &sigs)
|
||||
!= MUIV_Application_ReturnID_Quit)
|
||||
{
|
||||
if (sigs)
|
||||
{
|
||||
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
|
||||
if (sigs & SIGBREAKF_CTRL_C) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(window, MUIA_Window_Open, FALSE);
|
||||
MUI_DisposeObject(app);
|
||||
MUI_DeleteCustomClass(mcc);
|
||||
fail(NULL, NULL);
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Custom Class Methods
|
||||
|
||||
Beyond `AskMinMax` and `Draw`, custom classes often implement:
|
||||
|
||||
| Method | Purpose |
|
||||
|--------|---------|
|
||||
| `OM_NEW` | Initialize instance data when object is created |
|
||||
| `OM_DISPOSE` | Clean up instance data before deletion |
|
||||
| `OM_SET` | Handle attribute changes |
|
||||
| `OM_GET` | Handle attribute queries |
|
||||
| `MUIM_Setup` | Prepare for rendering (allocate resources) |
|
||||
| `MUIM_Cleanup` | Release rendering resources |
|
||||
| `MUIM_HandleEvent` | Handle raw input events |
|
||||
| `MUIM_DragQuery` | Accept or reject drag operations |
|
||||
| `MUIM_DragDrop` | Handle dropped objects |
|
||||
|
||||
## Inheritance Notes
|
||||
|
||||
- `DoSuperMethodA()` forwards a method to the superclass with the original message
|
||||
- `DoSuperMethod()` forwards with a variable argument list
|
||||
- `DoSuperNew()` is a helper for forwarding `OM_NEW` with tag lists
|
||||
- Always call the superclass first in `AskMinMax` and `Draw` unless you intend to completely replace its behavior
|
||||
|
||||
---
|
||||
|
||||
Previous: [Menus](08-menus.md)
|
||||
Next: [Events and Notifications](10-events-and-notifications.md)
|
||||
278
09_intuition/frameworks/mui/10-events-and-notifications.md
Normal file
278
09_intuition/frameworks/mui/10-events-and-notifications.md
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Events and Notifications
|
||||
|
||||
## The MUI Event Model
|
||||
|
||||
MUI replaces traditional callback registration with a declarative notification system. Instead of writing:
|
||||
|
||||
```c
|
||||
/* Traditional GUI toolkit */
|
||||
button_set_callback(myButton, on_click, myHandler);
|
||||
```
|
||||
|
||||
You write:
|
||||
|
||||
```c
|
||||
/* MUI */
|
||||
DoMethod(myButton, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
targetObj, 3, MUIM_CallHook, &myHook, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
This establishes a relationship: when the source attribute changes to a specific value, invoke a method on the target object.
|
||||
|
||||
## MUIM_Notify
|
||||
|
||||
The notification method signature:
|
||||
|
||||
```c
|
||||
DoMethod(sourceObj, MUIM_Notify,
|
||||
sourceAttribute, triggerValue,
|
||||
targetObj, numArgs, targetMethod, ...);
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `sourceObj` | Object to monitor |
|
||||
| `sourceAttribute` | Attribute to watch (e.g., `MUIA_Pressed`) |
|
||||
| `triggerValue` | Value that triggers the notification |
|
||||
| `targetObj` | Object to invoke the method on |
|
||||
| `numArgs` | Number of arguments passed to the target method |
|
||||
| `targetMethod` | Method to invoke (e.g., `MUIM_Application_ReturnID`) |
|
||||
| `...` | Additional arguments |
|
||||
|
||||
### Common Trigger Values
|
||||
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| `MUIV_EveryTime` | Trigger on any change, not just a specific value |
|
||||
| `TRUE` / `FALSE` | Trigger only when boolean attribute becomes true/false |
|
||||
| Specific value | Trigger only when attribute equals this exact value |
|
||||
|
||||
### Simple Example: Close Window
|
||||
|
||||
```c
|
||||
DoMethod(window, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
|
||||
app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
|
||||
```
|
||||
|
||||
When the window's close request flag becomes `TRUE` (user clicks the close gadget), the Application receives a ReturnID of `Quit`.
|
||||
|
||||
### Button Press
|
||||
|
||||
```c
|
||||
DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
app, 2, MUIM_Application_ReturnID, ID_SAVE);
|
||||
```
|
||||
|
||||
`MUIA_Pressed` becomes `FALSE` when the user releases the mouse button over the gadget. This is the standard way to detect button clicks.
|
||||
|
||||
### Slider Value Change
|
||||
|
||||
```c
|
||||
DoMethod(slider, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
|
||||
textObj, 3, MUIM_Set, MUIA_Text_Contents, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
Every time the slider value changes, update a text object. `MUIV_TriggerValue` passes the new slider value as the argument.
|
||||
|
||||
## Input Loops
|
||||
|
||||
MUI provides two methods for running the application's main loop.
|
||||
|
||||
### Modern Loop: MUIM_Application_NewInput
|
||||
|
||||
This is the preferred method. It is faster because it integrates signal waiting.
|
||||
|
||||
```c
|
||||
ULONG sigs = 0;
|
||||
|
||||
while (DoMethod(app, MUIM_Application_NewInput, &sigs)
|
||||
!= MUIV_Application_ReturnID_Quit)
|
||||
{
|
||||
if (sigs)
|
||||
{
|
||||
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
|
||||
if (sigs & SIGBREAKF_CTRL_C)
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
How it works:
|
||||
|
||||
1. `DoMethod(app, MUIM_Application_NewInput, &sigs)` processes pending input
|
||||
2. It returns a ReturnID if one was triggered, or 0
|
||||
3. It fills `sigs` with the signal mask the application should wait on
|
||||
4. Your code calls `Wait()` with those signals
|
||||
5. The loop repeats
|
||||
|
||||
This is significantly more efficient than the legacy loop because MUI tells you exactly which signals to wait for.
|
||||
|
||||
### Legacy Loop: MUIM_Application_Input
|
||||
|
||||
Older examples use this method. It is simpler but less efficient.
|
||||
|
||||
```c
|
||||
ULONG signals;
|
||||
BOOL running = TRUE;
|
||||
|
||||
while (running)
|
||||
{
|
||||
switch (DoMethod(app, MUIM_Application_Input, &signals))
|
||||
{
|
||||
case MUIV_Application_ReturnID_Quit:
|
||||
running = FALSE;
|
||||
break;
|
||||
|
||||
case ID_ABOUT:
|
||||
/* handle about */
|
||||
break;
|
||||
|
||||
case ID_SAVE:
|
||||
/* handle save */
|
||||
break;
|
||||
}
|
||||
|
||||
if (running && signals)
|
||||
Wait(signals);
|
||||
}
|
||||
```
|
||||
|
||||
The difference: `MUIM_Application_Input` handles waiting internally but returns a generic signal mask. `MUIM_Application_NewInput` gives you finer control.
|
||||
|
||||
## Return IDs
|
||||
|
||||
Return IDs are how notifications communicate with the input loop. MUI defines a standard quit ID:
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|----------|-------|---------|
|
||||
| `MUIV_Application_ReturnID_Quit` | -1 | Application should terminate |
|
||||
|
||||
You can define your own IDs:
|
||||
|
||||
```c
|
||||
#define ID_ABOUT 1
|
||||
#define ID_SAVE 2
|
||||
#define ID_OPEN 3
|
||||
```
|
||||
|
||||
Return IDs are triggered by notifications:
|
||||
|
||||
```c
|
||||
DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
app, 2, MUIM_Application_ReturnID, ID_SAVE);
|
||||
```
|
||||
|
||||
## Hooks
|
||||
|
||||
For complex actions that cannot be expressed as a simple method call, use hooks. A hook is a standard AmigaOS callback structure.
|
||||
|
||||
### Hook Structure
|
||||
|
||||
```c
|
||||
struct Hook
|
||||
{
|
||||
struct MinNode h_MinNode;
|
||||
ULONG (*h_Entry)(); /* function pointer */
|
||||
ULONG (*h_SubEntry)();
|
||||
APTR h_Data; /* user data */
|
||||
};
|
||||
```
|
||||
|
||||
### Defining a Hook
|
||||
|
||||
```c
|
||||
SAVEDS ASM LONG MyHookFunc(REG(a2) APTR obj, REG(a1) APTR param)
|
||||
{
|
||||
/* obj is the object that triggered the hook */
|
||||
/* param is the argument passed from the notification */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct Hook MyHook = {
|
||||
{ NULL, NULL }, (VOID *)MyHookFunc, NULL, NULL
|
||||
};
|
||||
```
|
||||
|
||||
### Invoking a Hook via Notification
|
||||
|
||||
```c
|
||||
DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
app, 3, MUIM_CallHook, &MyHook, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
### Hook Register Conventions
|
||||
|
||||
The MUI examples use register arguments for hook functions:
|
||||
|
||||
| Register | Parameter |
|
||||
|----------|-----------|
|
||||
| `a0` | struct Hook * |
|
||||
| `a2` | Object * (source object) |
|
||||
| `a1` | Msg / parameter |
|
||||
|
||||
Different compilers handle register keywords differently, so the examples use conditional macros:
|
||||
|
||||
```c
|
||||
#ifdef _DCC
|
||||
#define REG(x) __ ## x
|
||||
#define ASM
|
||||
#define SAVEDS __geta4
|
||||
#else
|
||||
#define REG(x) register __ ## x
|
||||
#if defined __MAXON__ || defined __GNUC__
|
||||
#define ASM
|
||||
#define SAVEDS
|
||||
#else
|
||||
#define ASM __asm
|
||||
#define SAVEDS __saveds
|
||||
#endif
|
||||
#endif
|
||||
```
|
||||
|
||||
## Notification Chains
|
||||
|
||||
Notifications can be chained. One notification can trigger an attribute change that causes another notification:
|
||||
|
||||
```c
|
||||
/* When checkbox is checked, enable the button */
|
||||
DoMethod(checkbox, MUIM_Notify, MUIA_Selected, TRUE,
|
||||
button, 3, MUIM_Set, MUIA_Disabled, FALSE);
|
||||
|
||||
/* When checkbox is unchecked, disable the button */
|
||||
DoMethod(checkbox, MUIM_Notify, MUIA_Selected, FALSE,
|
||||
button, 3, MUIM_Set, MUIA_Disabled, TRUE);
|
||||
```
|
||||
|
||||
## Common Notification Patterns
|
||||
|
||||
### Synchronize Two Widgets
|
||||
|
||||
```c
|
||||
/* Slider and numeric button show the same value */
|
||||
DoMethod(slider, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
|
||||
numButton, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);
|
||||
|
||||
DoMethod(numButton, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
|
||||
slider, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
### Update Window Title
|
||||
|
||||
```c
|
||||
DoMethod(string, MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime,
|
||||
window, 3, MUIM_Set, MUIA_Window_Title, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
### Enable Widget Based on Selection
|
||||
|
||||
```c
|
||||
DoMethod(listview, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
|
||||
app, 2, MUIM_CallHook, &UpdateButtonHook);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Previous: [Custom Classes](09-custom-classes.md)
|
||||
Next: [Advanced Patterns](11-advanced-patterns.md)
|
||||
282
09_intuition/frameworks/mui/11-advanced-patterns.md
Normal file
282
09_intuition/frameworks/mui/11-advanced-patterns.md
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Advanced Patterns
|
||||
|
||||
## Drag and Drop
|
||||
|
||||
MUI supports drag and drop between listviews and custom objects. The drag source initiates the operation; the destination decides whether to accept it.
|
||||
|
||||
### Enabling Drag and Drop on Lists
|
||||
|
||||
```c
|
||||
ListviewObject,
|
||||
MUIA_Listview_DragType, MUIV_Listview_DragType_Default,
|
||||
MUIA_Listview_List, ListObject,
|
||||
MUIA_List_DragSortable, TRUE,
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
`MUIA_List_DragSortable` allows the user to reorder list items by dragging.
|
||||
|
||||
### Custom Drag Query
|
||||
|
||||
For custom classes, implement `MUIM_DragQuery` to accept or reject drags:
|
||||
|
||||
```c
|
||||
ULONG DragQuery(struct IClass *cl, Object *obj, struct MUIP_DragDrop *msg)
|
||||
{
|
||||
if (msg->obj == obj)
|
||||
{
|
||||
/* Dragging onto ourselves - let superclass handle it */
|
||||
return DoSuperMethodA(cl, obj, msg);
|
||||
}
|
||||
else if (msg->obj == (Object *)muiUserData(obj))
|
||||
{
|
||||
/* Accept drags from our predefined source */
|
||||
return MUIV_DragQuery_Accept;
|
||||
}
|
||||
|
||||
/* Reject everything else */
|
||||
return MUIV_DragQuery_Refuse;
|
||||
}
|
||||
```
|
||||
|
||||
Return values:
|
||||
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| `MUIV_DragQuery_Accept` | Accept the drag |
|
||||
| `MUIV_DragQuery_Refuse` | Reject the drag |
|
||||
| `MUIV_DragQuery_Ask` | Ask user (rarely used) |
|
||||
|
||||
### Handling the Drop
|
||||
|
||||
Implement `MUIM_DragDrop` to process the actual drop:
|
||||
|
||||
```c
|
||||
ULONG DragDrop(struct IClass *cl, Object *obj, struct MUIP_DragDrop *msg)
|
||||
{
|
||||
/* msg->obj is the source, obj is the destination */
|
||||
/* Perform the drop operation */
|
||||
return DoSuperMethodA(cl, obj, msg);
|
||||
}
|
||||
```
|
||||
|
||||
## Settings Persistence
|
||||
|
||||
MUI provides a mechanism to save and restore the state of widgets using `Dataspace` objects.
|
||||
|
||||
### Saving Settings
|
||||
|
||||
```c
|
||||
APTR dataspace;
|
||||
|
||||
/* Create a dataspace to hold settings */
|
||||
dataspace = MUI_NewObject(MUIC_Dataspace, TAG_DONE);
|
||||
|
||||
/* Ask each object to export its settings */
|
||||
DoMethod(window, MUIM_Export, dataspace);
|
||||
|
||||
/* Save the dataspace to disk */
|
||||
/* (Implementation depends on your storage format) */
|
||||
```
|
||||
|
||||
### Loading Settings
|
||||
|
||||
```c
|
||||
/* Load dataspace from disk, then: */
|
||||
DoMethod(window, MUIM_Import, dataspace);
|
||||
|
||||
/* Update the UI */
|
||||
MUI_DisposeObject(dataspace);
|
||||
```
|
||||
|
||||
Many built-in MUI classes support `MUIM_Export` and `MUIM_Import` automatically. Custom classes need to implement these methods if they want to participate.
|
||||
|
||||
## Subtasks and Background Processing
|
||||
|
||||
For long-running operations, perform work in a separate task and update the UI periodically. The `Subtask.c` example demonstrates this with a fractal renderer.
|
||||
|
||||
### SubTask Structure
|
||||
|
||||
```c
|
||||
struct SubTask
|
||||
{
|
||||
struct Task *st_Task; /* Sub task pointer */
|
||||
struct MsgPort *st_Port; /* Allocated by sub task */
|
||||
struct MsgPort *st_Reply; /* Allocated by main task */
|
||||
APTR st_Data; /* Initial data */
|
||||
struct SubTaskMsg st_Message;
|
||||
};
|
||||
|
||||
struct SubTaskMsg
|
||||
{
|
||||
struct Message stm_Message;
|
||||
WORD stm_Command;
|
||||
APTR stm_Parameter;
|
||||
LONG stm_Result;
|
||||
};
|
||||
```
|
||||
|
||||
### Communication Protocol
|
||||
|
||||
```c
|
||||
#define STC_STARTUP -2
|
||||
#define STC_SHUTDOWN -1
|
||||
#define STC_START 0
|
||||
#define STC_STOP 1
|
||||
|
||||
LONG SendSubTaskMsg(struct SubTask *st, WORD command, APTR params)
|
||||
{
|
||||
st->st_Message.stm_Message.mn_ReplyPort = st->st_Reply;
|
||||
st->st_Message.stm_Message.mn_Length = sizeof(struct SubTaskMsg);
|
||||
st->st_Message.stm_Command = command;
|
||||
st->st_Message.stm_Parameter = params;
|
||||
st->st_Message.stm_Result = 0;
|
||||
|
||||
PutMsg(command == STC_STARTUP
|
||||
? &((struct Process *)st->st_Task)->pr_MsgPort
|
||||
: st->st_Port,
|
||||
(struct Message *)&st->st_Message);
|
||||
|
||||
WaitPort(st->st_Reply);
|
||||
GetMsg(st->st_Reply);
|
||||
|
||||
return st->st_Message.stm_Result;
|
||||
}
|
||||
```
|
||||
|
||||
### Spawning a Subtask
|
||||
|
||||
```c
|
||||
struct SubTask *SpawnSubTask(char *name, VOID (*func)(VOID), APTR data)
|
||||
{
|
||||
struct SubTask *st;
|
||||
|
||||
if (st = AllocVec(sizeof(struct SubTask), MEMF_PUBLIC | MEMF_CLEAR))
|
||||
{
|
||||
if (st->st_Reply = CreateMsgPort())
|
||||
{
|
||||
st->st_Data = data;
|
||||
|
||||
if (st->st_Task = CreateNewProcTags(
|
||||
NP_Entry, func,
|
||||
NP_Name, name,
|
||||
NP_Priority, 0,
|
||||
TAG_DONE))
|
||||
{
|
||||
/* Send startup message with SubTask pointer */
|
||||
SendSubTaskMsg(st, STC_STARTUP, st);
|
||||
return st;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* cleanup on error */
|
||||
return NULL;
|
||||
}
|
||||
```
|
||||
|
||||
### Updating the UI from the Subtask
|
||||
|
||||
The subtask should not directly call MUI methods. Instead, it signals the main task, which then updates the UI:
|
||||
|
||||
```c
|
||||
/* In subtask: calculate a line, then signal main task */
|
||||
Signal(mainTask, SIGF_SINGLE);
|
||||
|
||||
/* In main task input loop or notification handler: */
|
||||
/* Check for update flag and redraw affected area */
|
||||
```
|
||||
|
||||
Alternatively, use `MUIM_Application_PushMethod` to safely queue a method call from another task:
|
||||
|
||||
```c
|
||||
/* Thread-safe: push a method onto the application's queue */
|
||||
DoMethod(app, MUIM_Application_PushMethod, obj, 2, MUIM_Redraw, MADF_DRAWOBJECT);
|
||||
```
|
||||
|
||||
## BOOPSI Gadget Integration
|
||||
|
||||
MUI can host native BOOPSI gadgets through the `Boopsi` class:
|
||||
|
||||
```c
|
||||
Child, BoopsiObject,
|
||||
MUIA_Boopsi_ClassID, "gadgetclass",
|
||||
MUIA_Boopsi_MinWidth, 100,
|
||||
MUIA_Boopsi_MinHeight, 20,
|
||||
MUIA_Boopsi_Gadget, myGadget,
|
||||
TAG_DONE,
|
||||
```
|
||||
|
||||
This is useful when you have existing BOOPSI gadgets that you want to embed in a MUI layout.
|
||||
|
||||
## Complex UI State Management
|
||||
|
||||
When multiple widgets depend on the same state, use a Dataspace or a central model object with notifications:
|
||||
|
||||
```c
|
||||
/* Central model object (can be a simple Notify subclass) */
|
||||
APTR model = MUI_NewObject(MUIC_Notify,
|
||||
MUIA_UserData, initialValue,
|
||||
TAG_DONE);
|
||||
|
||||
/* Widget A reflects model state */
|
||||
DoMethod(model, MUIM_Notify, MUIA_UserData, MUIV_EveryTime,
|
||||
widgetA, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);
|
||||
|
||||
/* Widget B also reflects model state */
|
||||
DoMethod(model, MUIM_Notify, MUIA_UserData, MUIV_EveryTime,
|
||||
widgetB, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);
|
||||
|
||||
/* Changing the model updates both widgets */
|
||||
set(model, MUIA_UserData, newValue);
|
||||
```
|
||||
|
||||
## Notification Chains for Wizard UIs
|
||||
|
||||
For multi-step UIs, chain notifications to show/hide pages:
|
||||
|
||||
```c
|
||||
/* Show page 2 when Next is pressed */
|
||||
DoMethod(nextButton, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
page1, 3, MUIM_Set, MUIA_ShowMe, FALSE);
|
||||
|
||||
DoMethod(nextButton, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
page2, 3, MUIM_Set, MUIA_ShowMe, TRUE);
|
||||
|
||||
/* Show page 1 when Back is pressed */
|
||||
DoMethod(backButton, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
page2, 3, MUIM_Set, MUIA_ShowMe, FALSE);
|
||||
|
||||
DoMethod(backButton, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
page1, 3, MUIM_Set, MUIA_ShowMe, TRUE);
|
||||
```
|
||||
|
||||
## Using MUI_MakeObject
|
||||
|
||||
For simple widgets that don't need attributes at creation time, `MUI_MakeObject` provides a compact syntax:
|
||||
|
||||
```c
|
||||
/* Cycle gadget */
|
||||
APTR cycle = MUI_MakeObject(MUIO_Cycle, NULL, choices);
|
||||
|
||||
/* Radio buttons */
|
||||
APTR radio = MUI_MakeObject(MUIO_Radio, NULL, options);
|
||||
|
||||
/* Horizontal bar */
|
||||
APTR hbar = MUI_MakeObject(MUIO_HBar, 4);
|
||||
|
||||
/* Vertical bar */
|
||||
APTR vbar = MUI_MakeObject(MUIO_VBar, 4);
|
||||
|
||||
/* Menu strip from NewMenu */
|
||||
APTR strip = MUI_MakeObject(MUIO_MenustripNM, newMenu, 0);
|
||||
```
|
||||
|
||||
This reduces verbosity for simple cases.
|
||||
|
||||
---
|
||||
|
||||
Previous: [Events and Notifications](10-events-and-notifications.md)
|
||||
Next: [Reference Snippets](12-reference-snippets.md)
|
||||
428
09_intuition/frameworks/mui/12-reference-snippets.md
Normal file
428
09_intuition/frameworks/mui/12-reference-snippets.md
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
[← Home](../../../README.md) · [Intuition](../../README.md) · [Frameworks](../README.md)
|
||||
|
||||
# Reference Snippets
|
||||
|
||||
Quick copy-paste templates for common MUI patterns.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Minimal Application Skeleton](#minimal-application-skeleton)
|
||||
- [Window with Close Handler](#window-with-close-handler)
|
||||
- [Vertical Layout with Text and Button](#vertical-layout-with-text-and-button)
|
||||
- [Horizontal Button Row](#horizontal-button-row)
|
||||
- [Listview with Strings](#listview-with-strings)
|
||||
- [Slider with Value Display](#slider-with-value-display)
|
||||
- [Cycle Gadget](#cycle-gadget)
|
||||
- [Radio Buttons](#radio-buttons)
|
||||
- [Notification Patterns](#notification-patterns)
|
||||
- [Custom Class Boilerplate](#custom-class-boilerplate)
|
||||
- [Menu Definition Template](#menu-definition-template)
|
||||
- [Hook Function Template](#hook-function-template)
|
||||
- [AppWindow Drag and Drop](#appwindow-drag-and-drop)
|
||||
- [Input Loop Variants](#input-loop-variants)
|
||||
|
||||
## Minimal Application Skeleton
|
||||
|
||||
```c
|
||||
#include <libraries/mui.h>
|
||||
#include <clib/muimaster_protos.h>
|
||||
#include <clib/exec_protos.h>
|
||||
#include <clib/intuition_protos.h>
|
||||
#include <pragmas/muimaster_pragmas.h>
|
||||
#include <pragmas/exec_pragmas.h>
|
||||
#include <pragmas/intuition_pragmas.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct Library *MUIMasterBase;
|
||||
extern struct Library *SysBase;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
APTR app, window;
|
||||
|
||||
if (!(MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)))
|
||||
return 20;
|
||||
|
||||
app = ApplicationObject,
|
||||
MUIA_Application_Title , "App",
|
||||
MUIA_Application_Version , "$VER: App 1.0",
|
||||
MUIA_Application_Copyright , "Author",
|
||||
MUIA_Application_Author , "Author",
|
||||
MUIA_Application_Description, "Description",
|
||||
MUIA_Application_Base , "APP",
|
||||
|
||||
SubWindow, window = WindowObject,
|
||||
MUIA_Window_Title, "Window",
|
||||
MUIA_Window_ID , MAKE_ID('W','I','N','1'),
|
||||
WindowContents, VGroup,
|
||||
Child, TextObject,
|
||||
MUIA_Text_Contents, "Hello MUI",
|
||||
End,
|
||||
End,
|
||||
End,
|
||||
End;
|
||||
|
||||
if (!app)
|
||||
{
|
||||
CloseLibrary(MUIMasterBase);
|
||||
return 20;
|
||||
}
|
||||
|
||||
DoMethod(window, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
|
||||
app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
|
||||
|
||||
set(window, MUIA_Window_Open, TRUE);
|
||||
|
||||
{
|
||||
ULONG sigs = 0;
|
||||
while (DoMethod(app, MUIM_Application_NewInput, &sigs)
|
||||
!= MUIV_Application_ReturnID_Quit)
|
||||
{
|
||||
if (sigs)
|
||||
{
|
||||
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
|
||||
if (sigs & SIGBREAKF_CTRL_C) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(window, MUIA_Window_Open, FALSE);
|
||||
MUI_DisposeObject(app);
|
||||
CloseLibrary(MUIMasterBase);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Window with Close Handler
|
||||
|
||||
```c
|
||||
DoMethod(window, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
|
||||
app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
|
||||
```
|
||||
|
||||
## Vertical Layout with Text and Button
|
||||
|
||||
```c
|
||||
WindowContents, VGroup,
|
||||
Child, TextObject,
|
||||
TextFrame,
|
||||
MUIA_Background, MUII_TextBack,
|
||||
MUIA_Text_Contents, "\33cTitle",
|
||||
End,
|
||||
Child, StringObject,
|
||||
StringFrame,
|
||||
MUIA_String_Contents, "",
|
||||
End,
|
||||
Child, HGroup,
|
||||
Child, HSpace(0),
|
||||
Child, SimpleButton("OK"),
|
||||
Child, SimpleButton("Cancel"),
|
||||
Child, HSpace(0),
|
||||
End,
|
||||
End,
|
||||
```
|
||||
|
||||
## Horizontal Button Row
|
||||
|
||||
```c
|
||||
Child, HGroup,
|
||||
MUIA_Group_SameSize, TRUE,
|
||||
Child, SimpleButton("New"),
|
||||
Child, SimpleButton("Open"),
|
||||
Child, SimpleButton("Save"),
|
||||
Child, SimpleButton("Quit"),
|
||||
End,
|
||||
```
|
||||
|
||||
## Listview with Strings
|
||||
|
||||
```c
|
||||
APTR lv;
|
||||
|
||||
Child, lv = ListviewObject,
|
||||
MUIA_Listview_Input, TRUE,
|
||||
MUIA_Listview_List, ListObject,
|
||||
ReadListFrame,
|
||||
MUIA_List_ConstructHook, MUIV_List_ConstructHook_String,
|
||||
MUIA_List_DestructHook, MUIV_List_DestructHook_String,
|
||||
End,
|
||||
End,
|
||||
|
||||
/* Insert items */
|
||||
DoMethod(lv, MUIM_List_InsertSingle, "First", MUIV_List_Insert_Bottom);
|
||||
DoMethod(lv, MUIM_List_InsertSingle, "Second", MUIV_List_Insert_Bottom);
|
||||
|
||||
/* Clear */
|
||||
DoMethod(lv, MUIM_List_Clear);
|
||||
```
|
||||
|
||||
## Slider with Value Display
|
||||
|
||||
```c
|
||||
APTR slider, valueText;
|
||||
|
||||
Child, HGroup,
|
||||
Child, slider = SliderObject,
|
||||
MUIA_Numeric_Min, 0,
|
||||
MUIA_Numeric_Max, 100,
|
||||
MUIA_Numeric_Value, 50,
|
||||
MUIA_Slider_Horiz, TRUE,
|
||||
End,
|
||||
Child, valueText = TextObject,
|
||||
MUIA_Text_Contents, "50",
|
||||
End,
|
||||
End,
|
||||
|
||||
/* Sync slider to text */
|
||||
DoMethod(slider, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
|
||||
valueText, 3, MUIM_Set, MUIA_Text_Contents, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
## Cycle Gadget
|
||||
|
||||
```c
|
||||
static char *choices[] = { "Low", "Medium", "High", NULL };
|
||||
|
||||
Child, CycleObject,
|
||||
MUIA_Cycle_Entries, choices,
|
||||
End,
|
||||
```
|
||||
|
||||
## Radio Buttons
|
||||
|
||||
```c
|
||||
static char *options[] = { "Option 1", "Option 2", "Option 3", NULL };
|
||||
|
||||
Child, RadioObject,
|
||||
MUIA_Radio_Entries, options,
|
||||
End,
|
||||
```
|
||||
|
||||
## Notification Patterns
|
||||
|
||||
### Button Click -> Quit
|
||||
|
||||
```c
|
||||
DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
|
||||
```
|
||||
|
||||
### Button Click -> Custom Return ID
|
||||
|
||||
```c
|
||||
#define ID_SAVE 1
|
||||
|
||||
DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
app, 2, MUIM_Application_ReturnID, ID_SAVE);
|
||||
```
|
||||
|
||||
### Checkbox Enables/Disables Widget
|
||||
|
||||
```c
|
||||
DoMethod(checkbox, MUIM_Notify, MUIA_Selected, TRUE,
|
||||
widget, 3, MUIM_Set, MUIA_Disabled, FALSE);
|
||||
|
||||
DoMethod(checkbox, MUIM_Notify, MUIA_Selected, FALSE,
|
||||
widget, 3, MUIM_Set, MUIA_Disabled, TRUE);
|
||||
```
|
||||
|
||||
### String -> Window Title Sync
|
||||
|
||||
```c
|
||||
DoMethod(string, MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime,
|
||||
window, 3, MUIM_Set, MUIA_Window_Title, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
## Custom Class Boilerplate
|
||||
|
||||
```c
|
||||
struct MyData
|
||||
{
|
||||
LONG dummy;
|
||||
};
|
||||
|
||||
SAVEDS ULONG mAskMinMax(struct IClass *cl, Object *obj, struct MUIP_AskMinMax *msg)
|
||||
{
|
||||
DoSuperMethodA(cl, obj, msg);
|
||||
msg->MinMaxInfo->MinWidth += 100;
|
||||
msg->MinMaxInfo->DefWidth += 120;
|
||||
msg->MinMaxInfo->MaxWidth += 500;
|
||||
msg->MinMaxInfo->MinHeight += 40;
|
||||
msg->MinMaxInfo->DefHeight += 90;
|
||||
msg->MinMaxInfo->MaxHeight += 300;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SAVEDS ULONG mDraw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
|
||||
{
|
||||
DoSuperMethodA(cl, obj, msg);
|
||||
if (!(msg->flags & MADF_DRAWOBJECT))
|
||||
return 0;
|
||||
/* custom rendering here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
SAVEDS ASM ULONG MyDispatcher(REG(a0) struct IClass *cl,
|
||||
REG(a2) Object *obj,
|
||||
REG(a1) Msg msg)
|
||||
{
|
||||
switch (msg->MethodID)
|
||||
{
|
||||
case MUIM_AskMinMax: return mAskMinMax(cl, obj, (APTR)msg);
|
||||
case MUIM_Draw: return mDraw(cl, obj, (APTR)msg);
|
||||
}
|
||||
return DoSuperMethodA(cl, obj, msg);
|
||||
}
|
||||
|
||||
/* In main() */
|
||||
struct MUI_CustomClass *mcc;
|
||||
mcc = MUI_CreateCustomClass(NULL, MUIC_Area, NULL,
|
||||
sizeof(struct MyData), MyDispatcher);
|
||||
if (!mcc) /* handle error */;
|
||||
|
||||
APTR obj = NewObject(mcc->mcc_Class, NULL, TAG_DONE);
|
||||
|
||||
/* Cleanup */
|
||||
MUI_DeleteCustomClass(mcc);
|
||||
```
|
||||
|
||||
## Menu Definition Template
|
||||
|
||||
```c
|
||||
enum { MEN_PROJECT=1, MEN_ABOUT, MEN_QUIT, MEN_EDIT, MEN_CUT, MEN_COPY, MEN_PASTE };
|
||||
|
||||
static struct NewMenu MenuData[] = {
|
||||
{ NM_TITLE, "Project" , 0, 0, 0, (APTR)MEN_PROJECT },
|
||||
{ NM_ITEM , "About...", "?", 0, 0, (APTR)MEN_ABOUT },
|
||||
{ NM_ITEM , NM_BARLABEL, 0, 0, 0, (APTR)0 },
|
||||
{ NM_ITEM , "Quit" , "Q", 0, 0, (APTR)MEN_QUIT },
|
||||
{ NM_TITLE, "Edit" , 0, 0, 0, (APTR)MEN_EDIT },
|
||||
{ NM_ITEM , "Cut" , "X", 0, 0, (APTR)MEN_CUT },
|
||||
{ NM_ITEM , "Copy" , "C", 0, 0, (APTR)MEN_COPY },
|
||||
{ NM_ITEM , "Paste" , "V", 0, 0, (APTR)MEN_PASTE },
|
||||
{ NM_END , NULL , 0, 0, 0, (APTR)0 },
|
||||
};
|
||||
|
||||
/* Attach to window */
|
||||
MUIA_Window_Menustrip, MUI_MakeObject(MUIO_MenustripNM, MenuData, 0),
|
||||
|
||||
/* Handle in loop */
|
||||
case MEN_ABOUT: /* show about */ break;
|
||||
case MEN_QUIT: running = FALSE; break;
|
||||
```
|
||||
|
||||
## Hook Function Template
|
||||
|
||||
```c
|
||||
SAVEDS ASM LONG MyHookFunc(REG(a2) APTR obj, REG(a1) APTR param)
|
||||
{
|
||||
/* obj = object that triggered the hook */
|
||||
/* param = argument from notification */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct Hook MyHook = {
|
||||
{ NULL, NULL }, (VOID *)MyHookFunc, NULL, NULL
|
||||
};
|
||||
|
||||
/* Usage in notification */
|
||||
DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
|
||||
app, 3, MUIM_CallHook, &MyHook, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
## AppWindow Drag and Drop
|
||||
|
||||
```c
|
||||
/* Enable AppWindow */
|
||||
WindowObject,
|
||||
MUIA_Window_Title , "Drop Zone",
|
||||
MUIA_Window_ID , MAKE_ID('D','R','O','P'),
|
||||
MUIA_Window_AppWindow, TRUE,
|
||||
...
|
||||
End,
|
||||
|
||||
/* Hook to handle dropped icons */
|
||||
SAVEDS ASM LONG AppMsgFunc(REG(a2) APTR obj, REG(a1) struct AppMessage **x)
|
||||
{
|
||||
struct AppMessage *amsg = *x;
|
||||
struct WBArg *ap;
|
||||
int i;
|
||||
static char buf[256];
|
||||
|
||||
for (ap = amsg->am_ArgList, i = 0; i < amsg->am_NumArgs; i++, ap++)
|
||||
{
|
||||
NameFromLock(ap->wa_Lock, buf, sizeof(buf));
|
||||
AddPart(buf, ap->wa_Name, sizeof(buf));
|
||||
DoMethod(obj, MUIM_List_InsertSingle, buf, MUIV_List_Insert_Bottom);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct Hook AppMsgHook = {
|
||||
{ NULL, NULL }, (VOID *)AppMsgFunc, NULL, NULL
|
||||
};
|
||||
|
||||
/* Connect notification */
|
||||
DoMethod(listview, MUIM_Notify, MUIA_AppMessage, MUIV_EveryTime,
|
||||
listview, 3, MUIM_CallHook, &AppMsgHook, MUIV_TriggerValue);
|
||||
```
|
||||
|
||||
## Input Loop Variants
|
||||
|
||||
### Modern (Recommended)
|
||||
|
||||
```c
|
||||
ULONG sigs = 0;
|
||||
while (DoMethod(app, MUIM_Application_NewInput, &sigs)
|
||||
!= MUIV_Application_ReturnID_Quit)
|
||||
{
|
||||
if (sigs)
|
||||
{
|
||||
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
|
||||
if (sigs & SIGBREAKF_CTRL_C) break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Legacy with Return IDs
|
||||
|
||||
```c
|
||||
ULONG signals;
|
||||
BOOL running = TRUE;
|
||||
while (running)
|
||||
{
|
||||
switch (DoMethod(app, MUIM_Application_Input, &signals))
|
||||
{
|
||||
case MUIV_Application_ReturnID_Quit:
|
||||
running = FALSE;
|
||||
break;
|
||||
case ID_SAVE:
|
||||
/* handle save */
|
||||
break;
|
||||
}
|
||||
if (running && signals)
|
||||
Wait(signals);
|
||||
}
|
||||
```
|
||||
|
||||
### Legacy with Hooks (No Return IDs)
|
||||
|
||||
```c
|
||||
ULONG sigs = 0;
|
||||
while (DoMethod(app, MUIM_Application_NewInput, &sigs)
|
||||
!= MUIV_Application_ReturnID_Quit)
|
||||
{
|
||||
if (sigs)
|
||||
{
|
||||
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
|
||||
if (sigs & SIGBREAKF_CTRL_C) break;
|
||||
}
|
||||
}
|
||||
/* All actions handled via MUIM_CallHook notifications */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Previous: [Advanced Patterns](11-advanced-patterns.md)
|
||||
Back to [MUI Bootcamp](README.md)
|
||||
1160
09_intuition/frameworks/mui/README.md
Normal file
1160
09_intuition/frameworks/mui/README.md
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue