Skip to content

โ† 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 requirements
  • MUIM_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 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 Next: Events and Notifications