- 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
7 KiB
← Home · Intuition · Frameworks
Events and Notifications
The MUI Event Model
MUI replaces traditional callback registration with a declarative notification system. Instead of writing:
/* Traditional GUI toolkit */
button_set_callback(myButton, on_click, myHandler);
You write:
/* 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:
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
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
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
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.
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:
DoMethod(app, MUIM_Application_NewInput, &sigs)processes pending input- It returns a ReturnID if one was triggered, or 0
- It fills
sigswith the signal mask the application should wait on - Your code calls
Wait()with those signals - 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.
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:
#define ID_ABOUT 1
#define ID_SAVE 2
#define ID_OPEN 3
Return IDs are triggered by notifications:
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
struct Hook
{
struct MinNode h_MinNode;
ULONG (*h_Entry)(); /* function pointer */
ULONG (*h_SubEntry)();
APTR h_Data; /* user data */
};
Defining a Hook
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
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:
#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:
/* 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
/* 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
DoMethod(string, MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime,
window, 3, MUIM_Set, MUIA_Window_Title, MUIV_TriggerValue);
Enable Widget Based on Selection
DoMethod(listview, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
app, 2, MUIM_CallHook, &UpdateButtonHook);
Previous: Custom Classes Next: Advanced Patterns