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. AfterWMHI_ICONIFYset your copy to NULL and do not use it. The autodoc forWINDOW_Windowsays the same. - Re-read
WINDOW_SigMaskafter 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:
- When its value changes, the palette sends an
OM_NOTIFYwithPALETTE_Color. ICA_TARGETnames the receiver. The notification becomes anOM_UPDATEonBut_Object.ICA_MAPrenames the tags on the way:PALETTE_ColorbecomesBUTTON_TextPen(orBUTTON_BackgroundPenfor the second palette).- 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.
Related 3.2 changes to keep in mind¶
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¶
- ReAction GUI Toolkit
- window.class reference
- layout.gadget reference
- listbrowser.gadget reference
- arexx.class reference
- Workbench and icon.library V44–V47 for Developers
- GadTools V47 Additions
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