mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
- 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
278 lines
7 KiB
Markdown
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)
|