ReAction Examples Guide

The NDK 3.2 Examples/ directory holds 36 small ReAction programs: 33 single-file examples plus three in sub-directories (ARexx/, Backfill/, Bitmap/). They are the quickest way to learn a class, because each one is built around one gadget or image and shows the attributes and the event loop that class needs. This guide lists every example with the classes it uses and its main technique, explains the patterns they share, and walks through three of them in detail.

For the general ReAction model (window object, layout tree, RA_HandleInput()), read ReAction GUI Toolkit first.

Building the examples

Every source starts with a SAS/C build script in its first comment block, so it can be built with execute Example.c in a SAS/C environment:

;/* CheckBox Example
sc CheckBox.c LINK LIB lib:reaction.lib NOSTACKCHECK
quit
*/

Two variants occur:

Build line Meaning
sc X.c LINK LIB lib:reaction.lib NOSTACKCHECK the program relies on reaction.lib to open the class libraries ("SAS/C or DICE AutoInit can do this for you if linked with the supplied reaction.lib", Connect.c) and uses its helper functions (ChooserLabels(), RadioButtons(), ClickTabNodes(), …)
sc X.c LINK NOSTACKCHECK the program opens every class itself with OpenLibrary("window.class", …), OpenLibrary("gadgets/layout.gadget", …) and so on

NOSTACKCHECK matters whenever a hook is involved. String.c says so in its build comment: "NOSTACKCHECK is vital or the hook will crash when called!" Several sources also contain #ifdef _DCC blocks with a wbmain() stub for DICE.

The examples at a glance

Example Classes Technique
ARexx/ARexx.c arexx.class, window, layout, button ARexx host port AREXXDEMO from an ARexxCmd table with ReadArgs templates ("TEXT/F", "SYSTEM/S"); AM_EXECUTE launches a macro (the source asks for Demo.rexx, searched in the current directory and REXX:; the shipped script is named rx_me_demo.rexx); AREXX_ReplyHook; waits on both WINDOW_SigMask and AREXX_SigMask and calls RA_HandleRexx(). Ships rx_me_demo.rexx
Backfill/Backfill.c (+ ImageBackFill.c) window, layout, button, label, datatypes layer backfill hook that tiles a picture (LAYOUT_BackFill); AppWindow via WINDOW_AppPort/WINDOW_AppWindow/WINDOW_AppMsgHook; dropped picture replaces a child with LAYOUT_ModifyChild + CHILD_ReplaceObject; WINDOW_IDCMPHook for datatype DTA_Sync
Bevels.c window, layout, label every LAYOUT_BevelStyle (BVS_THIN, BVS_BUTTON, BVS_GROUP, BVS_FIELD, BVS_NONE, BVS_DROPBOX, BVS_STANDARD) side by side, printing the resulting sizes
Bitmap/Bitmap.c bitmap.image, window, layout, button loads buttons.iff with BITMAP_SourceFile, then cuts button images out of the same BITMAP_BitMap with BITMAP_OffsetX/Y and BITMAP_Select…; CHILD_NoDispose for a shared image
Buttons.c window, layout, button speed test: 100 buttons; ND=NODEFER/S argument toggles LAYOUT_DeferLayout
CheckBox.c checkbox, label, layout, window, button CHECKBOX_TextPlace left/right; a multi-line LabelObject in a BVS_FIELD group; iconify skeleton
Checkboxes.c window, layout, checkbox speed test with 50 checkboxes (NODEFER switch)
Chooser1.c chooser, label, layout, window, button pop-up list built with ChooserLabels() / FreeChooserLabels()
Chooser2.c chooser, layout, window, button "hidden" chooser (CHOOSER_Hidden) that is never added to the layout; ICA_TARGET, ICTARGET_IDCMP + WINDOW_IDCMPHook to read CHOOSER_Active from IDCMP_IDCMPUPDATE. Needs chooser V41.101+
ClickTab.c clicktab, layout, window tab strip from ClickTabNodes(), CLICKTAB_Current
Connect.c window, layout, button, palette, label BOOPSI interconnection: palettes drive a button's pens through ICA_TARGET + ICA_MAP with no application code
FuelGauge.c fuelgauge, label, layout, window, button progress bar with ticks (FUELGAUGE_Ticks, _ShortTicks, _Percent); WA_BusyPointer while updating FUELGAUGE_Level
GetColor.c getcolor, window, layout, button GCOLOR_REQUEST method opens the colour requester; reads back GETCOLOR_Color (0x00RRGGBB)
Glyph.c glyph.image, bevel, label, layout, window catalogue of GLYPH_* symbols (arrows, POPFILE, POPDRAWER, POPSCREENMODE, POPTIME, RADIOBUTTON…)
Integer.c integer, label, layout, window, button INTEGER_Minimum/Maximum/Arrows/MaxChars; ActivateLayoutGadget() puts the cursor in the first field
LED.c led.image, window, layout seven-segment display (LED_Pairs, LED_Signed, LED_Values); checks led.image ≥ 47.3 before using LED_Hexadecimal and LED_Raw; opens window.class with version 47
Label.c label.image label image mixing text, font changes and an image (LABEL_Image), drawn with DrawImageState() into a plain Intuition window, normal and selected
Layout1.c layout, button weighted layout without window.class: LayoutLimits(), AddGadget(), GA_RelWidth/RelHeight, handling LAYOUT_RequestRefresh/LAYOUT_RequestLayout from IDCMP_IDCMPUPDATE
Layout2.c layout, button, checkbox, window dynamic layout: LAYOUT_AddChild (with LAYOUT_Inverted for AddHead), LAYOUT_RemoveChild, LAYOUT_ModifyChild + CHILD_ReplaceObject at run time, then WM_RETHINK (falling back to WM_NEWPREFS)
ListBrowser1.c listbrowser, label multi-column list with ColumnInfo, editable cells (LISTBROWSER_EditNode/EditColumn), MakeVisible, AutoFit, VirtualWidth, images in nodes; each click on the close gadget switches to another configuration
ListBrowser2.c listbrowser, glyph, label, layout, window hierarchical tree (LISTBROWSER_Hierarchical, LBNA_Generation, LBFLG_HASCHILDREN|LBFLG_SHOWCHILDREN), custom pens (LBFLG_CUSTOMPENS)
ListView.c listview, scroller listview.gadget in a plain window; keys scroll it with LISTVIEW_ScrollUp/Down, page via LISTVIEW_Top/Visible/Total, toggle LISTVIEW_MultiSelect, change LISTVIEW_Spacing
Page.c clicktab, page (layout), listbrowser, string, button, label, window tabbed dialog: CLICKTAB_PageGroup holding a PageObject with PAGE_Add children, a record browser on one page
Palette.c palette palette.gadget in a plain window, opened with a local OpenClass() helper; PALETTE_NumColours from the screen depth
Palettes.c window, layout, palette speed test with 50 palettes (NODEFER switch)
PenMap.c penmap.image chunky image with its own palette (PENMAP_RenderData, PENMAP_SelectData, PENMAP_Palette, PENMAP_Screen)
PrintReq.c layout, button, checkbox, integer, radiobutton, label, window a complete print dialog: from/to/copies integers, radio buttons from RadioButtons(), collate checkbox with custom collate image data, WINDOW_LockHeight
RadioButton.c radiobutton, layout, window, button RadioButtons() / FreeRadioButtons(), RADIOBUTTON_Selected
ReqToolsPrefs.c window, layout, chooser, checkbox, integer, scroller, label, button preferences-editor skeleton: chooser selecting the requester type, checkboxes, integers and a scroller, Save/Use/Cancel row with LAYOUT_EvenSize, WPOS_CENTERMOUSE
Requester.c requester.class, window, layout, button RM_OPENREQ via a small OpenRequesterTags() wrapper; REQTYPE_INFO with label escape codes for bold/centre/fonts; REQTYPE_STRING input
Sketchboard.c sketchboard drawing gadget in a GimmeZeroZero window (SGA_Width/Height/Scale); keys switch SGA_Tool (SGTOOL_RECT, SGTOOL_ELLIPSE, …)
SpeedBar.c speedbar, button, label, layout, window toolbar from AllocSpeedButtonNode() (SBNA_Image, SBNA_Help, SBNA_Highlight); tooltips with WINDOW_HintInfo + WINDOW_GadgetHelp; resets SPEEDBAR_Window around iconify
String.c string, label, layout, window, button custom STRINGA_EditHook (a password hook) set up with RA_SetUpHook(); STRINGA_MinVisible/MaxChars
Tabs.c tabs.gadget tabs created by public class name (NewObject(NULL, "tabs.gadget", …)), added with AddGList() to a plain window
TapeDeck.c tapedeck, layout, window transport buttons; TDECK_Mode (BUT_PLAY, BUT_STOP, …); any command-line argument sets TDECK_Tape
ToolManagerPrefs.c window, layout, listbrowser, chooser, button, label larger prefs-editor skeleton: AppWindow drop handler adds dropped icons to a listbrowser, iconify with WINDOW_Icon, WINDOW_GadgetHelp

Classes with a header in Include_H/gadgets that no example uses are datebrowser, getfile, getfont, getscreenmode, gradientslider, colorwheel, slider, space, texteditor and virtual. Their autodocs are the reference.

Patterns that repeat

The iconify-ready skeleton

CheckBox, Chooser1, FuelGauge, Integer, RadioButton, String, SpeedBar, Layout2 and Page share one main loop. Learn it once:

AppPort = CreateMsgPort();
win_obj = WindowObject,
    WA_Title, "...",
    WA_CloseGadget, TRUE, WA_DragBar, TRUE, WA_DepthGadget, TRUE,
    WA_SizeGadget, TRUE, WA_Activate, TRUE,
    WINDOW_IconifyGadget, TRUE,
    WINDOW_IconTitle,     "CheckBox",
    WINDOW_AppPort,       AppPort,        /* needed for iconify */
    WINDOW_Position,      WPOS_CENTERMOUSE,
    WINDOW_ParentGroup,   VGroupObject, /* ... */ EndGroup,
EndWindow;

win = RA_OpenWindow(win_obj);
GetAttr(WINDOW_SigMask, win_obj, &signal);
app = 1L << AppPort->mp_SigBit;

while (!done)
{
    wait = Wait(signal | app | SIGBREAKF_CTRL_C);
    if (wait & SIGBREAKF_CTRL_C) break;

    while ((result = RA_HandleInput(win_obj, &code)) != WMHI_LASTMSG)
    {
        switch (result & WMHI_CLASSMASK)
        {
            case WMHI_CLOSEWINDOW: done = TRUE; break;
            case WMHI_GADGETUP:    /* result & WMHI_GADGETMASK = GA_ID */ break;
            case WMHI_ICONIFY:
                RA_Iconify(win_obj);
                win = NULL;
                break;
            case WMHI_UNICONIFY:
                win = RA_OpenWindow(win_obj);
                if (win) GetAttr(WINDOW_SigMask, win_obj, &signal);
                else     done = TRUE;
                break;
        }
    }
}
DisposeObject(win_obj);
DeleteMsgPort(AppPort);

Two points are easy to miss:

  • The struct Window * is only valid while the window is open. After WMHI_ICONIFY set your copy to NULL and do not use it. The autodoc for WINDOW_Window says the same.
  • Re-read WINDOW_SigMask after uniconify. The window reopens with a new IDCMP port. Backfill.c notes that this applies "if the program supported iconification and didn't use a shared IDCMP port between all windows".

Helper lists from reaction.lib

ChooserLabels()/ChooserLabelsA(), RadioButtons() and ClickTabNodes() turn string arrays into the node lists the classes want. AllocListBrowserNode() and AllocSpeedButtonNode() come from the class libraries themselves. Free every list after DisposeObject() of the window, never before. The examples call FreeChooserLabels() and FreeRadioButtons() last.

Changing a list while it is attached

ToolManagerPrefs.c shows the rule for list-based gadgets. Detach the list with ~0, modify it, then reattach it:

SetGadgetAttrs(lb, win, NULL, LISTBROWSER_Labels, ~0, TAG_END);
AddTail(list, AllocListBrowserNode(1, LBNCA_CopyText, TRUE,
                                      LBNCA_Text, name, TAG_END));
SetGadgetAttrs(lb, win, NULL, LISTBROWSER_Labels, list, TAG_END);

ListBrowser1.c does the same before swapping column layouts.

Speed tests and LAYOUT_DeferLayout

Buttons, Checkboxes and Palettes take a ND=NODEFER/S argument that sets LAYOUT_DeferLayout to FALSE. With deferral on (the default in all other examples), layout.gadget does not run GM_LAYOUT/GM_RENDER in input.device context but asks window.class to do it on the application's task. Layout1.c explains the reason: complex layouts would otherwise make "the pointer jumpy".

Walkthrough 1: Connect.c — gadgets talking to each other

Connect.c builds a button and two palettes and wires them together. When the user picks a colour, the button's text or background pen changes, with no code in the event loop. The wiring is the BOOPSI icclass mechanism:

struct TagItem MapFG2Button[] = { PALETTE_Color, BUTTON_TextPen,       TAG_END };
struct TagItem MapBG2Button[] = { PALETTE_Color, BUTTON_BackgroundPen, TAG_END };

StartMember, But_Object = ButtonObject,
    GA_Text, "_Inter-Connection Example",
    GA_ID,   ID_BUTTON,
EndMember,

StartMember, PaletteObject,
    GA_ID, ID_FOREGROUND,
    PALETTE_NumColors, 8,
    PALETTE_Color,     1,
    ICA_TARGET, But_Object,     /* object to connect to */
    ICA_MAP,    MapFG2Button,   /* tag mapping array */
EndMember,

How it works:

  1. When its value changes, the palette sends an OM_NOTIFY with PALETTE_Color.
  2. ICA_TARGET names the receiver. The notification becomes an OM_UPDATE on But_Object.
  3. ICA_MAP renames the tags on the way: PALETTE_Color becomes BUTTON_TextPen (or BUTTON_BackgroundPen for the second palette).
  4. The button applies the new pen and redraws itself.

Note the C trick: But_Object = ButtonObject, … EndMember assigns the object pointer inside the nested macro expression, so the palettes declared later can name it as their target. The target must be created before the object that points at it. The mapping arrays must stay valid as long as the objects exist; Connect.c keeps them on main()'s stack frame, which lives until DisposeObject().

The event loop only handles WMHI_CLOSEWINDOW and WMHI_GADGETUP. Everything else happens between the objects.

Walkthrough 2: Layout1.c — layout.gadget without window.class

Layout1.c deliberately avoids window.class ("we do NOT recommend this. Window.class offers automatic prefs refresh, and support for window backfills, iconification and keyboard control!"). It shows what window.class normally does for you, which is useful when you embed a layout in an existing Intuition program.

1. Build the layout group on its own (gParent = VGroupObject, … End) with LAYOUT_DeferLayout, TRUE, and weighted children (CHILD_WeightedWidth, 25/50/75/100).

2. Ask it how big it wants to be, then open a window of that size:

struct LayoutLimits Limits;
struct TextFont *font = OpenFont(Scr->Font);
LayoutLimits(gParent, &Limits, font, Scr);
CloseFont(font);

Win = OpenWindowTags(NULL,
    WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_IDCMPUPDATE,
    WA_InnerWidth,  Limits.MinWidth,
    WA_InnerHeight, Limits.MinHeight,
    WA_NoCareRefresh, TRUE,      /* only BOOPSI here */
    /* ... */ TAG_END);

3. Clamp the window size and attach the group with relative sizing, so it follows the window:

WindowLimits(Win, Limits.MinWidth + bw, Limits.MinHeight + bh,
                  Limits.MaxWidth + bw, Limits.MaxHeight + bh);
SetGadgetAttrs(gParent, NULL, NULL,
    GA_Top, Win->BorderTop, GA_Left, Win->BorderLeft,
    GA_RelWidth, -bw, GA_RelHeight, -bh, TAG_END);
AddGadget(Win, gParent, -1);
RefreshGList(gParent, Win, NULL, 1);

4. Do the deferred work that layout.gadget asks for through IDCMP_IDCMPUPDATE:

case IDCMP_IDCMPUPDATE:
    if (FindTagItem(LAYOUT_RequestRefresh, msg->IAddress))
        RefreshWindowFrame(Win);                /* also redraws gadgets */
    else if (FindTagItem(LAYOUT_RequestLayout, msg->IAddress))
        RethinkLayout(gParent, Win, NULL, FALSE);
    else if (FindTagItem(LAYOUT_RelVerify, msg->IAddress))
        switch (GetTagData(GA_ID, 0, msg->IAddress)) { /* button clicks */ }
    break;

LAYOUT_RelVerify is how a deferred layout reports what would otherwise be IDCMP_GADGETUP. The example also warns: IAddress of IDCMP_IDCMPUPDATE points to a tag list that is freed on reply. Either process it before ReplyMsg(), or CloneTagItems() it first and FreeTagItems() the copy later.

Walkthrough 3: Chooser2.c — notifications without GADGETUP

A "hidden" chooser is a pop-up menu that is not part of the layout. You show it yourself, for example from a button. Because it is never added to the window, it cannot send IDCMP_GADGETUP. Chooser2.c gets the selection through BOOPSI notification routed to the window's IDCMP:

Chooser_Object_Hidden = ChooserObject,
    GA_RelVerify,   TRUE,
    GA_ID,          ID_HIDDEN,
    CHOOSER_Labels, chooserlist,
    CHOOSER_DropDown, TRUE,
    CHOOSER_AutoFit,  TRUE,
    CHOOSER_Hidden,   TRUE,
    ICA_TARGET, ICTARGET_IDCMP,      /* notify via IDCMP_IDCMPUPDATE */
ChooserEnd;

Window_Object = WindowObject,
    WA_IDCMP, IDCMP_GADGETUP | IDCMP_GADGETDOWN | IDCMP_IDCMPUPDATE,
    WINDOW_IDCMPHook,     &idcmphook,  /* for BOOPSI notification */
    WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE,
    /* ... */
EndWindow;

window.class calls the hook for every IDCMP class listed in WINDOW_IDCMPHookBits, with the window object in A2 and the IntuiMessage in A1:

void __asm __saveds IDCMPFunc(register __a0 struct Hook *Hook,
                              register __a2 Object *Window,
                              register __a1 struct IntuiMessage *Msg)
{
    if (Msg->Class == IDCMP_IDCMPUPDATE &&
        GetTagData(GA_ID, 0, Msg->IAddress) == ID_HIDDEN)
    {
        ULONG active = GetTagData(CHOOSER_Active, -1, Msg->IAddress);
        /* use the selection */
    }
}

The same WINDOW_IDCMPHook mechanism is how Backfill.c catches the datatype's DTA_Sync notification to refresh the picture. A hook installed with WINDOW_AppMsgHook (Backfill.c, ToolManagerPrefs.c) receives AppMessages for icons dropped on the window, or on its icon while it is iconified, in the same register layout.

The examples date from the ClassAct/OS 3.5 era and use only a few V47 features (LED.c's version check is the exception). When adapting them for 3.2, see the "OS 3.2 additions" section of ReAction GUI Toolkit: WINDOW_NewMenu, LISTBROWSER_AutoWheel, WPOS_ENTIRESCREEN, WINDOW_Qualifier, and the wheel and tooltip support built on Intuition V47 BOOPSI and Input Extensions.

See Also


Sources: NDK 3.2 R4 Examples/*.c, Examples/ARexx/{ARexx.c,rx_me_demo.rexx}, Examples/Backfill/{Backfill.c,ImageBackFill.c,ImageBackFill.h}, Examples/Bitmap/Bitmap.c, Include_H/reaction/reaction_macros.h, Include_H/gadgets/layout.h, Include_H/intuition/icclass.h, Include_H/classes/{window,arexx}.h, Autodocs/window_cl.doc Updated: 2026-11-13