mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
doc: MUI framework documentation — whitepaper overview, SDK-derived architecture, layout mockups
- README.md: comprehensive whitepaper-style overview with historical context, paradigm shift analysis, BOOPSI comparison, version history (Stefan Stuntz as sole author 1.0-3.8), licensing model breakdown, parallel evolution timeline (NeXTSTEP/Qt/MUI convergence), community-sourced developer values - 02-architecture.md: complete rewrite from MUI 3.8 SDK sources — object lifecycle state machine, three-level resource binding, method dispatch chain, notification system with sequence diagrams, layout engine internals (3-pass constraint system), input handling, dynamic object linking, rendering model, tag ID namespace - 05-layout-system.md: Mermaid visual mockups for VGroup, HGroup, nested groups, column grids, scrollgroups, file requester real-world example, layout algorithm and resize sequence diagrams - frameworks/README.md: framework index with comparison table - All content in American English
This commit is contained in:
parent
c79d5e8459
commit
94a3ad1614
15 changed files with 4911 additions and 0 deletions
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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue