โ Home ยท Intuition ยท Frameworks
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.
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 requirementsMUIM_Draw- Render the object
AskMinMax
Called before layout. You must add your size requirements to what the superclass already calculated.
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.
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.
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
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
MyObj = NewObject(mcc->mcc_Class, NULL,
TextFrame,
MUIA_Background, MUII_BACKGROUND,
TAG_DONE);
Step 6: Cleanup
When your application exits, delete the custom class:
MUI_DeleteCustomClass(mcc);
Do this after disposing all objects of that class.
Complete Minimal Custom Class Example
#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 messageDoSuperMethod()forwards with a variable argument listDoSuperNew()is a helper for forwardingOM_NEWwith tag lists- Always call the superclass first in
AskMinMaxandDrawunless you intend to completely replace its behavior
Previous: Menus Next: Events and Notifications