amiga-bootcamp/09_intuition/frameworks/mui/11-advanced-patterns.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

7.3 KiB

← Home · Intuition · Frameworks

Advanced Patterns

Drag and Drop

MUI supports drag and drop between listviews and custom objects. The drag source initiates the operation; the destination decides whether to accept it.

Enabling Drag and Drop on Lists

ListviewObject,
    MUIA_Listview_DragType, MUIV_Listview_DragType_Default,
    MUIA_Listview_List, ListObject,
        MUIA_List_DragSortable, TRUE,
        End,
    End,

MUIA_List_DragSortable allows the user to reorder list items by dragging.

Custom Drag Query

For custom classes, implement MUIM_DragQuery to accept or reject drags:

ULONG DragQuery(struct IClass *cl, Object *obj, struct MUIP_DragDrop *msg)
{
    if (msg->obj == obj)
    {
        /* Dragging onto ourselves - let superclass handle it */
        return DoSuperMethodA(cl, obj, msg);
    }
    else if (msg->obj == (Object *)muiUserData(obj))
    {
        /* Accept drags from our predefined source */
        return MUIV_DragQuery_Accept;
    }

    /* Reject everything else */
    return MUIV_DragQuery_Refuse;
}

Return values:

Value Meaning
MUIV_DragQuery_Accept Accept the drag
MUIV_DragQuery_Refuse Reject the drag
MUIV_DragQuery_Ask Ask user (rarely used)

Handling the Drop

Implement MUIM_DragDrop to process the actual drop:

ULONG DragDrop(struct IClass *cl, Object *obj, struct MUIP_DragDrop *msg)
{
    /* msg->obj is the source, obj is the destination */
    /* Perform the drop operation */
    return DoSuperMethodA(cl, obj, msg);
}

Settings Persistence

MUI provides a mechanism to save and restore the state of widgets using Dataspace objects.

Saving Settings

APTR dataspace;

/* Create a dataspace to hold settings */
dataspace = MUI_NewObject(MUIC_Dataspace, TAG_DONE);

/* Ask each object to export its settings */
DoMethod(window, MUIM_Export, dataspace);

/* Save the dataspace to disk */
/* (Implementation depends on your storage format) */

Loading Settings

/* Load dataspace from disk, then: */
DoMethod(window, MUIM_Import, dataspace);

/* Update the UI */
MUI_DisposeObject(dataspace);

Many built-in MUI classes support MUIM_Export and MUIM_Import automatically. Custom classes need to implement these methods if they want to participate.

Subtasks and Background Processing

For long-running operations, perform work in a separate task and update the UI periodically. The Subtask.c example demonstrates this with a fractal renderer.

SubTask Structure

struct SubTask
{
    struct Task    *st_Task;    /* Sub task pointer */
    struct MsgPort *st_Port;    /* Allocated by sub task */
    struct MsgPort *st_Reply;   /* Allocated by main task */
    APTR            st_Data;    /* Initial data */
    struct SubTaskMsg st_Message;
};

struct SubTaskMsg
{
    struct Message stm_Message;
    WORD           stm_Command;
    APTR           stm_Parameter;
    LONG           stm_Result;
};

Communication Protocol

#define STC_STARTUP  -2
#define STC_SHUTDOWN -1
#define STC_START     0
#define STC_STOP      1

LONG SendSubTaskMsg(struct SubTask *st, WORD command, APTR params)
{
    st->st_Message.stm_Message.mn_ReplyPort = st->st_Reply;
    st->st_Message.stm_Message.mn_Length    = sizeof(struct SubTaskMsg);
    st->st_Message.stm_Command              = command;
    st->st_Message.stm_Parameter            = params;
    st->st_Message.stm_Result               = 0;

    PutMsg(command == STC_STARTUP
               ? &((struct Process *)st->st_Task)->pr_MsgPort
               : st->st_Port,
           (struct Message *)&st->st_Message);

    WaitPort(st->st_Reply);
    GetMsg(st->st_Reply);

    return st->st_Message.stm_Result;
}

Spawning a Subtask

struct SubTask *SpawnSubTask(char *name, VOID (*func)(VOID), APTR data)
{
    struct SubTask *st;

    if (st = AllocVec(sizeof(struct SubTask), MEMF_PUBLIC | MEMF_CLEAR))
    {
        if (st->st_Reply = CreateMsgPort())
        {
            st->st_Data = data;

            if (st->st_Task = CreateNewProcTags(
                    NP_Entry, func,
                    NP_Name, name,
                    NP_Priority, 0,
                    TAG_DONE))
            {
                /* Send startup message with SubTask pointer */
                SendSubTaskMsg(st, STC_STARTUP, st);
                return st;
            }
        }
    }
    /* cleanup on error */
    return NULL;
}

Updating the UI from the Subtask

The subtask should not directly call MUI methods. Instead, it signals the main task, which then updates the UI:

/* In subtask: calculate a line, then signal main task */
Signal(mainTask, SIGF_SINGLE);

/* In main task input loop or notification handler: */
/* Check for update flag and redraw affected area */

Alternatively, use MUIM_Application_PushMethod to safely queue a method call from another task:

/* Thread-safe: push a method onto the application's queue */
DoMethod(app, MUIM_Application_PushMethod, obj, 2, MUIM_Redraw, MADF_DRAWOBJECT);

BOOPSI Gadget Integration

MUI can host native BOOPSI gadgets through the Boopsi class:

Child, BoopsiObject,
    MUIA_Boopsi_ClassID, "gadgetclass",
    MUIA_Boopsi_MinWidth, 100,
    MUIA_Boopsi_MinHeight, 20,
    MUIA_Boopsi_Gadget, myGadget,
    TAG_DONE,

This is useful when you have existing BOOPSI gadgets that you want to embed in a MUI layout.

Complex UI State Management

When multiple widgets depend on the same state, use a Dataspace or a central model object with notifications:

/* Central model object (can be a simple Notify subclass) */
APTR model = MUI_NewObject(MUIC_Notify,
    MUIA_UserData, initialValue,
    TAG_DONE);

/* Widget A reflects model state */
DoMethod(model, MUIM_Notify, MUIA_UserData, MUIV_EveryTime,
    widgetA, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);

/* Widget B also reflects model state */
DoMethod(model, MUIM_Notify, MUIA_UserData, MUIV_EveryTime,
    widgetB, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);

/* Changing the model updates both widgets */
set(model, MUIA_UserData, newValue);

Notification Chains for Wizard UIs

For multi-step UIs, chain notifications to show/hide pages:

/* Show page 2 when Next is pressed */
DoMethod(nextButton, MUIM_Notify, MUIA_Pressed, FALSE,
    page1, 3, MUIM_Set, MUIA_ShowMe, FALSE);

DoMethod(nextButton, MUIM_Notify, MUIA_Pressed, FALSE,
    page2, 3, MUIM_Set, MUIA_ShowMe, TRUE);

/* Show page 1 when Back is pressed */
DoMethod(backButton, MUIM_Notify, MUIA_Pressed, FALSE,
    page2, 3, MUIM_Set, MUIA_ShowMe, FALSE);

DoMethod(backButton, MUIM_Notify, MUIA_Pressed, FALSE,
    page1, 3, MUIM_Set, MUIA_ShowMe, TRUE);

Using MUI_MakeObject

For simple widgets that don't need attributes at creation time, MUI_MakeObject provides a compact syntax:

/* Cycle gadget */
APTR cycle = MUI_MakeObject(MUIO_Cycle, NULL, choices);

/* Radio buttons */
APTR radio = MUI_MakeObject(MUIO_Radio, NULL, options);

/* Horizontal bar */
APTR hbar = MUI_MakeObject(MUIO_HBar, 4);

/* Vertical bar */
APTR vbar = MUI_MakeObject(MUIO_VBar, 4);

/* Menu strip from NewMenu */
APTR strip = MUI_MakeObject(MUIO_MenustripNM, newMenu, 0);

This reduces verbosity for simple cases.


Previous: Events and Notifications Next: Reference Snippets