Skip to content

Latest commit

 

History

History
1043 lines (774 loc) · 34.1 KB

File metadata and controls

1043 lines (774 loc) · 34.1 KB

FormCast Function Reference

Every FormCast surface is documented here. Variable functions (@FORM*) return a value through TCC's variable expansion; commands (plain FORMEVENTS etc.) write to stdout for do ... in /p consumption. All names are case-insensitive.

Conventions

  • h is a form handle from @FORMOPEN (e.g. L:1234:5).
  • ctrl is the control id you supplied to @FORMADD. Use . to address the form itself (form-level properties).
  • Numeric error codes returned in the function buffer:
  • 20100 invalid handle
  • 20101 bad arguments
  • 20102 unknown control type
  • 20103 unknown control id
  • 20104 unknown form property
  • 20105 I/O failure
  • 20106 JSON parse failure
  • 20107 unknown @FORMSIMULATE action

Lifecycle

@FORMOPEN[type,name,x,y,w,h]

Allocate a new form descriptor. Returns the handle string on success or empty buffer on failure.

@FORMCLOSE[h]

Close the form, fire any bound close event, and tear down the realized window. Returns 0 on success.

@FORMSHOW[h[,mode]]

Realize the form and display it. mode is empty/visible for non-modal, modal for modal-with-no-auto-close, modal:N for modal with an N-millisecond WinForms.Timer auto-dismiss. Returns 0 non-modal, the integer DialogResult for modal.

@FORMVERSION[]

Plugin version string (e.g. 1.0.0).

@FORMSAVEIMAGE[h,path]

Render the form to a PNG file at path. The form is realized lazily if needed but never displayed (off-screen rendering via Control.DrawToBitmap). Returns 0 on success.

@FORMSAVECOMPOSITE[path,h1,h2,...]

Render multiple realized forms into a single composite PNG. Each form is drawn at its current screen position relative to the bounding rectangle of all supplied forms. Gaps between windows are transparent. Useful for capturing multi-window layouts as a single screenshot.

Example (capture the designer's three windows):

set RC=%@formsavecomposite[%TEMP\designer.png,%hTool,%hCanvas,%hProps]

The README screenshots are generated by examples/screenshots/capture-readme-images.btm which uses @FORMSAVECOMPOSITE for the designer composite image.

Returns 0 on success.

@FORMFOCUS[h] / @FORMFOCUS[TCC]

Move foreground window focus to a realized form, or pass the literal string TCC to focus the TCC console window. Returns 0 on success.

@FORMSENDMESSAGE[h,msg,wparam,lparam]

Send a raw Win32 message to the realized form's HWND. msg accepts decimal or 0x-prefixed hex (e.g. 0x10 for WM_CLOSE). Returns the LRESULT as a decimal string.

@FORMHITTEST[h,x,y]

Return the control id at the given form-relative pixel position, or empty string if nothing is there. Used by the designer to map mouse clicks to controls.

@FORMSETENV[name,value]

Write a value into the BTM caller's variable scope via the SetEVariable P/Invoke. Pass an empty value to delete the variable. Returns 0 on success.

@FORMCMD[command line]

Execute a TCC command on the CallbackWorker thread via TakeCmd.Command. The entire argument string (including commas) is passed as-is to TCC. Returns the TCC command's return code.


Build

@FORMADD[h,id,TYPE,x,y,w,h[,text]]

Add a control to the form. Recognized TYPEs:

Basic: LABEL, EDIT, BUTTON, CHECKBOX, RADIO, LINKLABEL, MASKEDTEXTBOX, PICTUREBOX, TOGGLE

Numeric/Date: NUMERICUPDOWN, DOMAINUPDOWN, TRACKBAR, PROGRESSBAR, DATETIMEPICKER, MONTHCALENDAR

List/Tree/Grid: LISTBOX, CHECKEDLISTBOX, COMBOBOX, LISTVIEW, TREEVIEW, DATAGRID

Text: MEMO, RICHMEMO

Container: PANEL, GROUPBOX, SPLITCONTAINER, FLOWPANEL, TABLEPANEL, TABCONTROL, TABPAGE

App chrome: MENUSTRIP, TOOLBAR, STATUSBAR, CONTEXTMENU, SEPARATOR (toolbar child only)

Specialized: WEBBROWSER, PROPERTYGRID, HSCROLLBAR, VSCROLLBAR

Use the slash-id form (panel1/btn) to add a control nested inside any container type.

@FORMSET[h,ctrl,prop,value]

Set a property on the form (ctrl=.) or a control. Universal props: type, id, x, y, width, height, text. Per-type pseudo-props documented below. Unknown props land in the control's prop bag for forward compatibility.

Live-update behavior: on a realized (shown) form, the following props update the visible control immediately:

  • text -- labels, buttons, edits, and other text-bearing controls
  • value, min, max, checked -- ProgressBar, TrackBar, NumericUpDown, ScrollBar, CheckBox, Toggle
  • position (x:y), size (w:h) -- control bounds
  • splitterdistance -- SplitContainer divider position

@FORMGET[h,ctrl,prop]

Read a property. Empty buffer on miss. selecteditem (LISTVIEW), text (RICHMEMO), position, size are computed reads.


GROUPBOX

GROUPBOX is a container like PANEL but with a titled border. Critical for radio button grouping: WinForms only allows one selected RadioButton per parent container, so two groups of radios on the same form need two GROUPBOXes. Uses the same slash-id child syntax as PANEL.

COMBOBOX

COMBOBOX is a dropdown selection control. Items are added via the additem pseudo-prop (same verb as LISTVIEW, stored as _cb.item.N internally).

Prop Effect
additem append an item to the dropdown list
style dropdown (editable, default), list (non-editable), simple (always-open list)
selectedindex 0-based index of the pre-selected item

Read via @FORMGET[h,cmb,selecteditem] to get the currently selected text.

TABCONTROL and TABPAGE

TABCONTROL holds TABPAGE children. Each tab page has its own child controls. Use the slash-id syntax to nest:

%@formadd[%h,tabs,TABCONTROL,8,8,400,260,]
%@formadd[%h,tabs/general,TABPAGE,0,0,0,0,General]
%@formadd[%h,tabs/general/lbl,LABEL,12,12,200,20,Option:]
%@formadd[%h,tabs/advanced,TABPAGE,0,0,0,0,Advanced]

TABPAGE position/size are ignored (the TabControl manages layout). The text argument becomes the tab label.

NUMERICUPDOWN

Numeric spinner control. Props:

Prop Effect
min minimum value (default 0)
max maximum value (default 100)
value current value (default 0)
decimals decimal places (default 0 for integer)

Fires a change event with the new value on every spin.

DATETIMEPICKER

Date/time selection control. Props:

Prop Effect
format long (default), short, time, custom
customformat format string when format=custom (e.g. yyyy-MM-dd)

Fires a change event with the new value in ISO 8601 format.

LINKLABEL

A label with a clickable hyperlink. Set the url prop to the target:

%@formadd[%h,link,LINKLABEL,12,12,200,20,Visit our website]
%@formset[%h,link,url,https://example.com]

Fires a click event when the link is clicked. The URL opens in the default browser.

PICTUREBOX

Display an image from a file path. Set the image prop:

%@formadd[%h,img,PICTUREBOX,12,12,200,150,]
%@formset[%h,img,image,C:\path\to\logo.png]

Supports PNG, JPG, BMP, GIF. Uses Zoom size mode (aspect-preserving).

TRACKBAR

Horizontal or vertical slider.

Prop Effect
min minimum value (default 0)
max maximum value (default 100)
value current value (default 0)
tickfrequency tick mark interval (default 10)
orientation horizontal (default) or vertical

Fires a change event with the new value on every slide.

LISTBOX

Simple scrollable list without columns. Simpler than LISTVIEW when you just need a plain list of strings. Items added via additem.

Fires a change event with the selected item text on selection change.

CHECKEDLISTBOX

ListBox where each item has a checkbox. Items added via additem. Fires a change event with index:true or index:false on each check/uncheck.

MASKEDTEXTBOX

TextBox with an input mask. Set the mask prop:

%@formadd[%h,phone,MASKEDTEXTBOX,12,12,200,24,]
%@formset[%h,phone,mask,(000) 000-0000]

Standard WinForms mask characters: 0=digit, L=letter, A=alphanumeric.

MONTHCALENDAR

Visual month-view calendar. Fires a change event with the selected date in yyyy-MM-dd format.

TREEVIEW

Hierarchical tree with expand/collapse. Nodes are added via addnode with path:text format:

%@formset[%h,tree,addnode,root:Root Node]
%@formset[%h,tree,addnode,root/child1:First Child]
%@formset[%h,tree,addnode,root/child2:Second Child]
%@formset[%h,tree,addnode,root/child1/leaf:Leaf Node]

The path uses / as a separator; intermediate nodes are created automatically. addnode works both before and after @FORMSHOW (live-apply). To clear all nodes, set text to empty: %@formset[%h,tree,text,]. Fires a change event with the selected node name.

SPLITCONTAINER

Resizable divider between two panels. The first two children go into Panel1 and Panel2 respectively. Props:

Prop Effect
orientation vertical (left/right, default) or horizontal (top/bottom)
splitterdistance initial position of the splitter in pixels (also live-settable after @FORMSHOW)

TOGGLE (on/off switch)

An on/off slider switch control. Fires a change event with true or false when toggled. Read state with @FORMGET[h,tgl,checked].

%@formadd[%h,tgl,TOGGLE,12,12,50,24,]
%@formset[%h,tgl,checked,true]

SEPARATOR (toolbar child)

A visual separator for use as a child of TOOLBAR. Equivalent to using text "-" on a LABEL child, but expressed as its own type:

%@formadd[%h,tb/sep1,SEPARATOR,0,0,0,0,]

Appearance properties

These props work on any control (or on the form itself via ctrl=.) through @FORMSET:

Prop Format Effect
backcolor named color or #RRGGBB background color (e.g. Blue, DarkBlue, Highlight, #336699)
forecolor named color or #RRGGBB foreground/text color (e.g. HighlightText, #FFFFFF)
font family:size[:style] font face, size, optional style (e.g. Segoe UI:11:bold, Consolas:10:italic)

Named colors include all System.Drawing.Color names plus the system colors Highlight, HighlightText, Window, WindowText, Control, ControlText, etc.

Font inheritance: ApplyFontRecursive propagates a form-level font to all child controls that have not been individually styled, matching the .NET Framework 4.8 font inheritance model.

Theme and dark mode

Prop Where Effect
theme form (.) system (default), dark, or light. Applies live -- switching theme re-paints the form and all controls immediately. Dark mode sets the DWM title bar attribute for a native dark title bar on Windows 10+.
darkmode form (.) Legacy alias: %@formset[%h,.,darkmode,1] is equivalent to %@formset[%h,.,theme,dark].

Keyboard shortcuts (form-level)

Prop Where Effect
acceptbutton form (.) Wire the Enter key to a named button: %@formset[%h,.,acceptbutton,btnOK]
cancelbutton form (.) Wire the Escape key to a named button: %@formset[%h,.,cancelbutton,btnCancel]

Designer keyboard shortcuts (active when design_mode=1):

Key Action
Arrow keys Nudge selected control(s) by grid size (or 1px if grid off)
Delete Delete selected control(s)
Ctrl+Z Undo
Ctrl+Y Redo
Ctrl+C Copy
Ctrl+X Cut
Ctrl+V Paste
Ctrl+S Save
Ctrl+N New canvas
Ctrl+O Open template
Escape Deselect all

Stock icons

FormCast includes a library of 216 built-in icons across 16 categories (File, Edit, Navigation, Actions, Formatting, Status, Objects, Controls, Alignment, Layout, Arrows, Media, Development, Communication, Shapes, Misc).

Apply a stock icon to any image-capable control (Button, Label, PictureBox, CheckBox, RadioButton, LinkLabel):

%@formset[%h,btn,stockicon,FileNew]
%@formset[%h,lbl,stockicon,StatusInfo]

FORMICONS [filter] (command)

List all stock icons with their categories, one per line via stdout. Optional filter limits the output to icons matching the filter string. Consume with do ... in /p:

do icon in /p formicons
  echo %icon
enddo
do icon in /p formicons File
  echo %icon
enddo

Additional properties

Prop Where Effect
anchor any control Resize anchoring: top+bottom+left+right (any combination). Controls anchored to opposite edges stretch with the parent.
autoscroll PANEL Enable auto-scrolling when child controls exceed the panel bounds: %@formset[%h,panel1,autoscroll,1]
textfromfile any text control Set the control's text from a file path, bypassing TCC variable expansion: %@formset[%h,memo,textfromfile,C:\data\log.txt]
textfromvar any text control Set the control's text from a TCC variable name (read at set-time): %@formset[%h,lbl,textfromvar,MYVAR]
designtarget PROPERTYGRID Bind a PropertyGrid to a control or form descriptor for live editing: %@formset[%h,pg,designtarget,btnOK]

MENUSTRIP (app menu bar)

A menu bar at the top of the form. Children (via slash-id) become top-level menus; grandchildren become menu items. Text "-" creates a separator. Each item fires a click event on the per-form queue.

%@formadd[%h,menu,MENUSTRIP,0,0,0,0,]
%@formadd[%h,menu/file,LABEL,0,0,0,0,File]
%@formadd[%h,menu/file/open,LABEL,0,0,0,0,Open...]
%@formadd[%h,menu/file/save,LABEL,0,0,0,0,Save]
%@formadd[%h,menu/file/sep1,LABEL,0,0,0,0,-]
%@formadd[%h,menu/file/exit,LABEL,0,0,0,0,Exit]
%@formadd[%h,menu/edit,LABEL,0,0,0,0,Edit]
%@formadd[%h,menu/edit/copy,LABEL,0,0,0,0,Copy]
%@formadd[%h,menu/edit/paste,LABEL,0,0,0,0,Paste]
%@formbind[%h,open,click,gosub :on_open]
%@formbind[%h,exit,click,gosub :on_exit]

Position/size args on menu children are ignored.

CONTEXTMENU (right-click menu)

Same item model as MENUSTRIP but attached as a right-click menu. Set the form-level contextmenu prop to the CONTEXTMENU control's id to attach it to the form background.

TOOLBAR (ToolStrip)

Real toolbar with proper styling. Children with type BUTTON become ToolStripButtons; type SEPARATOR or text "-" creates a separator; type LABEL creates a ToolStripLabel. Each button fires a click event.

Toolbar buttons use click-through activation: clicking a button fires immediately even when the parent form is not the active window. This is essential for multi-window apps (like the visual designer) where the user selects a control on one form then clicks a toolbar button on another without needing to click twice.

%@formadd[%h,tb,TOOLBAR,0,0,0,0,]
%@formadd[%h,tb/btnNew,BUTTON,0,0,0,0,New]
%@formadd[%h,tb/sep1,LABEL,0,0,0,0,-]
%@formadd[%h,tb/btnOpen,BUTTON,0,0,0,0,Open]
%@formbind[%h,btnNew,click,gosub :on_new]

STATUSBAR (StatusStrip)

Status bar at the bottom of the form. Children become ToolStripStatusLabel panels. Set spring=1 on a child to make it fill remaining space.

%@formadd[%h,sb,STATUSBAR,0,0,0,0,]
%@formadd[%h,sb/msg,LABEL,0,0,0,0,Ready]
%@formset[%h,sb/msg,spring,1]
%@formadd[%h,sb/pos,LABEL,0,0,0,0,Ln 1 Col 1]

DATAGRID (DataGridView)

Full spreadsheet-like grid. Columns via addcolumn name:width:type (types: text, checkbox, combobox, button, link, image). Rows via addrow cell0:cell1:....

Prop Effect
addcolumn add a column (name:width:type)
addrow add a row (cell0:cell1:...)
readonly 1 makes the grid read-only
allowaddrows 1 allows the user to add rows
allowdeleterows 1 allows the user to delete rows

FLOWPANEL (FlowLayoutPanel)

Auto-flowing container. Children are laid out left-to-right (or top-to-bottom) and wrap automatically. Props:

Prop Effect
direction lefttoright (default) or toptobottom
nowrap 1 disables wrapping

TABLEPANEL (TableLayoutPanel)

Grid-based container. Props:

Prop Effect
rows number of rows (default 2)
cols number of columns (default 2)

Children specify their position via row and col props.

WEBBROWSER

Embedded browser control. Set url prop to navigate, or set text to load HTML content directly:

%@formadd[%h,web,WEBBROWSER,0,0,600,400,]
%@formset[%h,web,url,https://example.com]

DOMAINUPDOWN

String spinner (like NUMERICUPDOWN but for text lists). Items via additem.

HSCROLLBAR / VSCROLLBAR

Standalone scroll bars. Props: min, max, value.

PROPERTYGRID

Property editor control (like Visual Studio's Properties window).

ToolTip (hover text)

Not a control type — a prop that works on ANY control:

%@formset[%h,btn,tooltip,Click here to save your work]

@FORMNOTIFY (system tray icon)

Manage a notification area icon. Actions:

%@formnotify[show,My App] :: show tray icon
%@formnotify[show,My App,icon.ico] :: with custom icon
%@formnotify[balloon,Alert,Check this,info] :: balloon notification
%@formnotify[hide] :: remove tray icon

Balloon icon types: none, info, warning, error.


Common dialogs

These are standalone dispatch verbs, not control types. Each shows a native Windows dialog and returns the result as a string. All are suppressed in headless mode (return empty immediately).

@FORMOPENDIALOG[title[,filter[,initialdir]]]

Native "Open File" dialog. Returns the selected file path, or empty on cancel. Filter format: Text files:*.txt:All files:*.* (colon- separated pairs; pipe also works but is unsafe in BTM SET).

@FORMSAVEDIALOG[title[,filter[,initialdir]]]

Native "Save File" dialog. Same args and return as FORMOPENDIALOG.

@FORMFOLDERDIALOG[description[,initialdir]]

Native "Browse for Folder" dialog. Returns the selected directory path, or empty on cancel.

@FORMCOLORDIALOG[[initialcolor]]

Native color picker. Returns #RRGGBB hex string, or empty on cancel. Pass an initial color as #RRGGBB or a named color.

@FORMFONTDIALOG[]

Native font picker. Returns family:size[:style] (e.g. Consolas:12:Bold), or empty on cancel.


Running as a standalone Windows app

@FORMCONSOLE[action]

Control the TCC console window's visibility:

Action Effect
hide Hide the console completely. The BTM keeps running invisibly.
show Restore the console (from hidden or minimized).
minimize Minimize the console to the taskbar.

The "BTM as a Windows app" pattern:

A BTM can run as a standalone app by accepting a /app argument. When /app is passed, the BTM loads the plugin, hides the TCC console via @FORMCONSOLE[hide], and runs in a single process with no visible console window:

@echo off
setlocal

:: Load the plugin
call formcast-check.btm load

:: /app mode: hide the TCC console, show in taskbar
if "%1" == "/app" set RC=%@formconsole[hide]

set h=%@formopen[form,myapp,100,100,600,400]
set RC=%@formset[%h,.,title,My Application]
iff "%1" == "/app" then
  set RC=%@formset[%h,.,showintaskbar,1]
  set RC=%@formset[%h,.,icon,myapp.ico]
endiff
:: ... add controls, show, event loop ...

Setup: set the FORMCAST_DLL environment variable so the plugin can be found by any BTM:

set FORMCAST_DLL=C:\path\to\bin\FormCast.dll

A shared formcast-check.btm helper handles load, unload, and exit cleanup. It checks (in order): already loaded, FORMCAST_DLL env var, same-directory fallback. Every example BTM calls it instead of hardcoding a path. When a BTM is launched from Explorer (%_parent contains explorer), the script performs plugin /u + exit on close for clean teardown.

From a TCC prompt (normal development):

plugin /l FormCast.dll
myapp.btm

As a standalone app (no visible console):

myapp.btm /app

Desktop shortcut:

For zero-flash launch, use start /inv in the shortcut target:

Field Value
Target "C:\path\to\tcc.exe" /c start /inv /pgm "C:\path\to\tcc.exe" /c "C:\path\to\myapp.btm" /app
Start in Directory containing the BTM
Run Minimized
Icon Browse to your .ico file

The outer tcc.exe /c runs start /inv which launches the app in a fully invisible TCC session. The shortcut's Run=Minimized hides the brief outer TCC window. The shortcut must target tcc.exe /c -- pointing directly at the .btm file will not pass arguments correctly.

Alternatively, skip the launcher and target the app directly:

Target "C:\path\to\tcc.exe" /c "C:\path\to\myapp.btm" /app

This works but may flash the console briefly before @FORMCONSOLE[hide] runs.

For multi-window apps, use the owner property to group windows so they activate together from the taskbar:

set RC=%@formset[%hChild,.,owner,%hMain]

App window props (form-level)

Prop Effect
icon Load a .ico file as the form's title bar and taskbar icon: %@formset[%h,.,icon,myapp.ico]
showintaskbar 1 shows the form in the Windows taskbar. Default is 0 (hidden) since most forms are utility dialogs under TCC. Set to 1 for the "BTM as a standalone app" pattern.
owner Set the WinForms Owner relationship. Value is a form handle. Owned forms always appear above their owner, activate together when the owner is clicked in the taskbar, and close when the owner closes. Example: %@formset[%hChild,.,owner,%hMain]
runtimecontextmenu Attach a CONTEXTMENU to the form or a control at the WinForms level WITHOUT storing it in the descriptor. @FORMSAVE produces a clean template. Value is ctrlid (same form) or handle:ctrlid (cross-form). Used by the designer to give canvas controls a right-click menu that lives on the tool window.

Template event bindings

Event bindings can be stored in a template as inert data using _bind.* props on each control:

%@formset[%h,btnOK,_bind.click,gosub :on_ok]
%@formset[%h,btnCancel,_bind.click,gosub :on_cancel]
%@formset[%h,.,_bind.close,gosub :on_exit]

These are saved by @FORMSAVE as part of the control's property bag. They are NOT automatically activated on @FORMLOAD — the loading BTM must explicitly call @FORMAPPLYBINDINGS:

@FORMAPPLYBINDINGS[h[,validate]]

Walk the form descriptor and register every _bind.* prop as a live @FORMBIND entry. Returns the count of bindings applied.

set h=%@formload[myform.jsonc]
set count=%@formapplybindings[%h]
echo Applied %count bindings
%@formshow[%h]

The designer saves bindings but does NOT call this function, so it never trips over missing subroutine labels. The end-user BTM calls it after load to activate the wiring.

Pass validate as the second argument to check binding targets before activating. Returns 20106 if any target fails validation.

Recognized binding props: any _bind.<event> key where <event> is one of the events listed in the Event Reference section (e.g. _bind.click, _bind.change, _bind.focus, _bind.blur, _bind.dblclick, _bind.keypress, _bind.close, _bind.mousedown, _bind.mouseup, _bind.keydown, _bind.keyup, _bind.resize, _bind.dragenter, _bind.dragdrop, _bind.mouseenter, _bind.mouseleave, _bind.scroll, _bind.columnclick, _bind.cellclick, _bind.beforeexpand, _bind.afterexpand, _bind.beforecollapse, _bind.aftercollapse).


Designer primitives Form-level (ctrl=.):

Prop Meaning
design_mode 1/0 toggle the designer mode flag
selected string id of the currently selected control (read)
selectedall space-separated ids of all multi-selected controls (read)
selectioncount number of selected controls (read)
controllist JSON array of {"id":"...","type":"..."} for all controls (read)
controls number of top-level controls (read)
gridsize snap grid size in pixels (read/write, default 8)
refreshdesign force repaint of selection overlay after programmatic changes (write)

Control-level:

Prop Format Effect
position x:y absolute set X,Y
size w:h absolute set Width,Height
moveby dx:dy delta move
resizeby dw:dh delta resize

All four operations apply live to realized controls (the visual position/size updates immediately). position and size are also readable via @FORMGET and return the same :-separated pair.

Clipboard operations (form-level, ctrl=.):

Prop Value Effect
clipcopy control id Copy one control (and its children) to clipboard
clipcopy (empty) Copy all multi-selected controls to clipboard
clipcut control id Copy one control to clipboard and delete it
clipcut (empty) Copy all multi-selected controls and delete them
clippaste (empty) Paste clipboard contents onto the form

ID handling on paste:

  • If the original ID is available (e.g. after cut), it is reused.
  • If the original ID already exists (copy+paste), a new unique ID is generated by incrementing the trailing number (Label1 -> Label2).
  • Children of containers are recursively renamed with unique IDs.
  • Multiple pastes produce sequential IDs (Button2, Button3, etc.).

LISTVIEW pseudo-props Set on a control with TYPE=LISTVIEW:

Prop Format Notes
addcolumn name:width:type append column. Types: text, number, date, size, icon
additem col0:col1:col2:... append a row
clear (empty) wipe columns and items
multiselect 1/0 toggle MultiSelect
sort colName:asc or colName:desc initial sort

Read via @FORMGET[h,lst,selecteditem] to get the first non-icon column of the currently selected row.


MEMO and RICHMEMO

MEMO is a Multiline=true TextBox. Prop bag flags:

Prop Effect
readonly 1 makes it read-only
nowrap 1 disables word wrap and adds horizontal scroll
appendtext append a line to the realized control (live-only; does not update the descriptor's text field)

RICHMEMO is a WPF RichTextBox in a WinForms ElementHost. It adds three live ops that mutate the realized control:

Prop Format Effect
appendcolor `text colorName`
appendstyle `text bold+italic+underline`
loadrules `regex color,regex

@FORMGET[h,rm,text] returns the live document as plain text.


State and events

@FORMSTATE[h]

Bitmask of the form's runtime state:

Bit Meaning
1 visible
2 enabled
4 focused
8 modal (ShowDialog in progress)
32 events_pending (queue is non-empty)

-1 for an invalid handle. Bit 32 is the polling target for on condition-driven scripts.

@FORMBIND[h,ctrl,event,command]

Register a TCC command to run when the named event fires on the named control. Pass an empty ctrl for form-level events such as close. Pass an empty command to clear a binding.

See the Event Reference section below for the full list of available events and their data formats.

@FORMSIMULATE[h,ctrl,action[,value]]

Synthesize an event for tests. Actions: click, type, settext, check, uncheck, focus, blur, dblclick, keypress.

FORMEVENTS [h|""] [scope] (command)

Drain the per-form event queue and write handle kind ctrl data lines to stdout. Empty handle drains every form. Consume with do ev in /p formevents.

FORMPIPE handle ctrlid [color] (command)

Read stdin line by line and append each line to the named MEMO or RICHMEMO control on the realized form. Exits on EOF (when the upstream pipe closes). For RICHMEMO, the optional color argument sets the foreground brush for every appended line (any WPF color name or #RRGGBB hex). Consume with a TCC pipe:

dir /s /b | FORMPIPE %h memo
type build.log | FORMPIPE %h rm Red

This is the complement of FORMEVENTS: FORMEVENTS streams events OUT of a form; FORMPIPE streams data INTO a form.


Event Reference

FormCast events are powered by the EventWiringTable, a registry-based system where each event is a named entry mapping a .NET event to a data-extraction lambda. Adding a new event to the system requires a single registration call -- no plumbing changes anywhere else.

When an event fires, FormCast enqueues a FormEvent record with a data string whose format depends on the event type. The tables below document every registered event and its data format.

Common events (all controls)

These events are available on every control type:

Event Data format Description
focus (empty) Control received keyboard focus
blur (empty) Control lost keyboard focus
dblclick (empty) Double-click on the control
mousedown x:y:button:clicks Mouse button pressed. button is Left, Right, or Middle; clicks is the click count (1 for single, 2 for double)
mouseup x:y Mouse button released at the given position
mouseenter (empty) Mouse pointer entered the control bounds
mouseleave (empty) Mouse pointer left the control bounds
keydown keycode:shift:ctrl:alt Key pressed. keycode is the integer Keys value; modifier flags are 1/0
keyup keycode:shift:ctrl:alt Key released. Same format as keydown
resize width:height Control was resized
dragenter formats Drag operation entered the control. formats is a pipe-separated list of available data formats (e.g. Text|FileDrop)
dragdrop x:y:data Data was dropped on the control at the given position

Control-specific click event

The click event fires only on controls where a click is a meaningful user action:

Control type Data format
BUTTON (empty)
LINKLABEL (empty)
PICTUREBOX (empty)
PANEL (empty)
LISTVIEW (empty)

Menu items (MENUSTRIP, CONTEXTMENU children) and TOOLBAR buttons also fire click.

Control-specific change event

The change event fires when a control's value is modified by the user. The data format varies by control type:

Control type Data format Example
CHECKBOX true or false true
RADIO true or false true
EDIT (TextBox) current text Hello world
TOGGLE (ToggleSwitch) true or false false
LABEL current text (rare -- programmatic change)
COMBOBOX selected text Option B
NUMERICUPDOWN numeric value 42
DATETIMEPICKER ISO 8601 date 2026-04-10T00:00:00
TRACKBAR integer value 75
CHECKEDLISTBOX index:true or index:false 2:true
LISTBOX selected text Item three
TREEVIEW selected node name child1
MONTHCALENDAR yyyy-MM-dd 2026-04-10
DATAGRID (DataGridView) row:col 3:1

TextBox-specific events

Event Data format Description
keypress single character Fires for each printable character typed. Useful for input filtering or live validation.

ScrollBar / TrackBar events

Event Data format Description
scroll value:type (ScrollBar) or value (TrackBar) Fires during scrolling. type is the ScrollEventType name (e.g. SmallIncrement, LargeDecrement, ThumbTrack). TrackBar omits the type.

ListView events

Event Data format Description
columnclick column index (0-based) A column header was clicked. Useful for implementing sort-on-click.

TreeView events

Event Data format Description
beforeexpand node name Fires before a node expands.
afterexpand node name Fires after a node expands.
beforecollapse node name Fires before a node collapses.
aftercollapse node name Fires after a node collapses.

DataGridView events

Event Data format Description
cellclick row:col A specific cell was clicked (0-based row and column indices).

Form-level events

Event Data format Description
close (empty) The form is closing. Bind on ctrl=. or empty ctrl.

Extensibility

The EventWiringTable is a dictionary keyed by (controlType, eventName). Each entry specifies the .NET event to subscribe and a lambda that extracts the data string from the EventArgs. Adding a new event for any control type is a single Register() call -- no changes needed in the realizer, dispatcher, or event queue.


Templates

@FORMSAVE[h,path]

Serialize the form descriptor to a JSONC file at path. Returns 0 on success.

@FORMLOAD[file[,vars]]

Deserialize a JSONC template into a fresh form. Optional vars is a key=value|key=value|... string consumed by ${var} substitution. Returns the new handle or empty buffer.

@FORMIMPORT[h,file[,vars]]

Append all controls from file to the form h. Atomic on id collision: rolls back and returns 20103.

@FORMRELAYOUT[h]

Re-run the layout manager named in the form's layout property against the current control list, mutating positions in place.


Plugin and host

@FORMVERSION[]

Plugin version string.

@FORMLOG[level[,path]]

Configure plugin logging. level is one of off, error, warn, info, debug, trace (case-insensitive). path is the log file (required when level is not off). The file is opened in append mode with read-sharing so you can tail -f it from another console while the plugin runs. Returns 0 on success.

Log levels (lowest to highest verbosity):

Level What it captures
off Nothing (default)
error Plugin failures, exceptions
warn Unexpected conditions that don't fail
info High-level actions (formopen, formclose, formshow)
debug Property sets/gets, event dispatch, control add
trace Every FORMEVENTS drain, every poll cycle

Example:

set RC=%@formlog[debug,%TEMP\formcast.log]
:: ... run your BTM ...
set RC=%@formlog[off]

FORMICONS [filter] (command)

Stream every stock icon name with its category, one per line. Optional filter limits the output to matching icons.

The cross-process FormCast.Host.exe daemon (Phase 10) is the plumbing that will host Global\ scoped forms in v1.x; it builds in v1 but the global registry decorator that talks to it is a follow-up.