amiga-bootcamp/09_intuition/frameworks/mui/10-events-and-notifications.md
Ilia Sharin 94a3ad1614 doc: MUI framework documentation — whitepaper overview, SDK-derived architecture, layout mockups
- README.md: comprehensive whitepaper-style overview with historical context,
  paradigm shift analysis, BOOPSI comparison, version history (Stefan Stuntz
  as sole author 1.0-3.8), licensing model breakdown, parallel evolution
  timeline (NeXTSTEP/Qt/MUI convergence), community-sourced developer values
- 02-architecture.md: complete rewrite from MUI 3.8 SDK sources — object
  lifecycle state machine, three-level resource binding, method dispatch chain,
  notification system with sequence diagrams, layout engine internals
  (3-pass constraint system), input handling, dynamic object linking,
  rendering model, tag ID namespace
- 05-layout-system.md: Mermaid visual mockups for VGroup, HGroup, nested
  groups, column grids, scrollgroups, file requester real-world example,
  layout algorithm and resize sequence diagrams
- frameworks/README.md: framework index with comparison table
- All content in American English
2026-04-23 16:46:58 -04:00

278 lines
7 KiB
Markdown

[← 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)