amiga-bootcamp/09_intuition/frameworks/mui/08-menus.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

5.8 KiB

← Home · Intuition · Frameworks

Menus

Menu System Architecture

MUI's menu system consists of three class types:

Class Role
Menustrip Root container holding all menus
Menu A single pull-down menu (e.g., "Project")
Menuitem An individual item within a menu

A Menustrip is attached to either a specific Window (via MUIA_Window_Menustrip) or to the Application (via MUIA_Application_Menustrip). When attached to the Application, all windows inherit the menu unless overridden.

Using GadTools NewMenu

The easiest way to define menus is with the familiar GadTools NewMenu structure. MUI converts this into its own object tree automatically.

Define the Menu Structure

enum {
    MEN_PROJECT = 1,
    MEN_ABOUT,
    MEN_QUIT,
    MEN_EDIT,
    MEN_CUT,
    MEN_COPY,
    MEN_PASTE
};

static struct NewMenu MenuData[] = {
    { NM_TITLE, "Project" , 0, 0, 0, (APTR)MEN_PROJECT },
    { NM_ITEM , "About...", "?", 0, 0, (APTR)MEN_ABOUT   },
    { NM_ITEM , NM_BARLABEL, 0, 0, 0, (APTR)0            },
    { NM_ITEM , "Quit"    , "Q", 0, 0, (APTR)MEN_QUIT    },

    { NM_TITLE, "Edit"    , 0, 0, 0, (APTR)MEN_EDIT     },
    { NM_ITEM , "Cut"     , "X", 0, 0, (APTR)MEN_CUT      },
    { NM_ITEM , "Copy"    , "C", 0, 0, (APTR)MEN_COPY     },
    { NM_ITEM , "Paste"   , "V", 0, 0, (APTR)MEN_PASTE    },

    { NM_END, NULL, 0, 0, 0, (APTR)0 },
};

Create and Attach the Menu

app = ApplicationObject,
    ...
    SubWindow, win = WindowObject,
        MUIA_Window_Title    , "Menus",
        MUIA_Window_ID       , MAKE_ID('M','E','N','1'),
        MUIA_Window_Menustrip, strip = MUI_MakeObject(MUIO_MenustripNM, MenuData, 0),
        WindowContents, VGroup,
            ...
            End,
        End,
    End;

MUI_MakeObject(MUIO_MenustripNM, MenuData, 0) parses the NewMenu array and returns a Menustrip object.

Handling Menu Selections

Method 1: Return IDs in the Input Loop

Each menu item's UserData becomes its Return ID. In the main loop, check for it:

while (running)
{
    switch (DoMethod(app, MUIM_Application_Input, &signals))
    {
        case MUIV_Application_ReturnID_Quit:
            running = FALSE;
            break;

        case MEN_ABOUT:
            /* show about box */
            break;

        case MEN_CUT:
            /* perform cut */
            break;
    }

    if (running && signals)
        Wait(signals);
}

Method 2: Notifications

Bind menu items directly to actions using MUIM_Notify:

APTR aboutItem;

/* Find the item using its userdata */
aboutItem = (APTR)DoMethod(strip, MUIM_FindUserData, MEN_ABOUT);

if (aboutItem)
{
    DoMethod(aboutItem, MUIM_Notify, MUIA_Menuitem_Trigger, MUIV_EveryTime,
        app, 2, MUIM_Application_ReturnID, MEN_ABOUT);
}

Method 3: Hooks

For more complex actions, use MUIM_CallHook:

SAVEDS ASM LONG AboutFunc(REG(a2) APTR obj, REG(a1) APTR msg)
{
    MUI_Request(app, win, 0, "About", "*OK",
        "MyApp\nVersion 1.0\nBy Author");
    return 0;
}

static struct Hook AboutHook = { {0,0}, (VOID *)AboutFunc, NULL, NULL };

/* In setup: */
DoMethod(aboutItem, MUIM_Notify, MUIA_Menuitem_Trigger, MUIV_EveryTime,
    app, 3, MUIM_CallHook, &AboutHook, MUIV_TriggerValue);

Dynamic Menu Manipulation

Enabling and Disabling Items

/* Disable a menu item */
set(menuItem, MUIA_Menuitem_Enabled, FALSE);

/* Enable it again */
set(menuItem, MUIA_Menuitem_Enabled, TRUE);

Checkmarks and Radio Groups

Use standard GadTools flags in the NewMenu structure:

#define RB CHECKIT

static struct NewMenu MenuData[] = {
    { NM_TITLE, "Settings"                 , 0 ,0             ,0,(APTR)MEN_SETTINGS },
    { NM_ITEM , "Hardware"                , 0 ,NM_ITEMDISABLED,0,(APTR)MEN_HARDWARE },
    { NM_SUB  ,   "A1000"                  ,"1",RB|CHECKED,2|4|8 ,(APTR)MEN_A1000    },
    { NM_SUB  ,   "A2000"                  ,"2",RB         ,1|4|8 ,(APTR)MEN_A2000    },
    { NM_SUB  ,   "A3000"                  ,"3",RB         ,1|2|8 ,(APTR)MEN_A3000    },
    { NM_SUB  ,   "A4000"                  ,"4",RB         ,1|2|4 ,(APTR)MEN_A4000    },
    { NM_END  , NULL                       , 0 ,0             ,0,(APTR)0            },
};

CHECKIT makes the item checkable. CHECKED pre-checks it. The mutual exclude field (e.g., 2|4|8) defines radio groups: items with overlapping bits cannot be checked simultaneously.

Toggle Items

Use MENUTOGGLE for items that toggle on/off independently:

#define TG CHECKIT|MENUTOGGLE

{ NM_SUB, "Option", "O", TG, 0, (APTR)MEN_OPTION },

Creating Menus Programmatically

Instead of NewMenu, you can build the menu tree manually:

MUIA_Window_Menustrip, MenustripObject,
    Child, MenuObject,
        MUIA_Menu_Title, "Project",
        Child, MenuitemObject,
            MUIA_Menuitem_Title, "About...",
            MUIA_Menuitem_Shortcut, "?",
            End,
        Child, MenuitemObject,
            MUIA_Menuitem_Title, NM_BARLABEL,
            End,
        Child, MenuitemObject,
            MUIA_Menuitem_Title, "Quit",
            MUIA_Menuitem_Shortcut, "Q",
            End,
        End,
    End,

This is more verbose but gives you direct access to every menu item object for notifications.

Menu Best Practices

  • Always provide keyboard shortcuts for common actions
  • Use NM_BARLABEL to separate logical groups
  • Keep the NewMenu userdata values unique across the entire menu
  • Attach menus to the Application if all windows share the same menu
  • Attach menus to individual Windows only when menus differ per window
  • Use MUIM_FindUserData on the Menustrip to locate individual items for notifications

Previous: Windows and Applications Next: Custom Classes