All names are in module tv. Import it qualified (IMPORT tv;) or with
FROM tv IMPORT .... Every drawing call draws into an off-screen cell
buffer; nothing reaches the terminal until Present (or Finish).
| procedure | description |
|---|---|
Init() : BOOLEAN |
enter raw mode + alternate screen, install SIGWINCH handler, create the desktop. FALSE if stdin is not a tty. |
Done |
leave the alternate screen and restore the terminal. |
Cols() / Rows() : INTEGER |
current screen size. |
Resized() : BOOLEAN |
TRUE if the terminal size changed or SIGWINCH fired since the last call (also re-clears the screen). Call it in your loop. |
Finish |
DrawTree + Present (redraw everything and flush). |
DrawTree |
draw the desktop, all views and the menu bar into the cell buffer. |
Present |
flush the diff to the terminal. |
16 colours: Black Blue Green Cyan Red Magenta Brown LightGray DarkGray
LightBlue LightGreen LightCyan LightRed LightMagenta Yellow White.
A(fg, bg : INTEGER; blink : BOOLEAN) : Attr builds an attribute.
Frame styles: SingleFrame, DoubleFrame.
Clear(attr), Fill(x,y,w,h, cp, attr), DrawCh(x,y,cp,attr),
DrawText(x,y, s, attr), DrawBox(r, style, attr), DrawShadow(r, attr),
SetCursor(x,y), HideCursor. cp is a Unicode code point (CARDINAL);
ASCII text can use VAL(CARDINAL, ORD(ch)).
TextLen(s) : INTEGER, CopyText(dest, s), AppendText(dest, s),
PadText(dest, s, width), IntToText(dest, n, width).
TYPE Event = RECORD
kind : EventKind; (* evNone evKey evMouse *)
key : INTEGER; (* kChar kUp kDown kLeft kRight kHome kEnd
kPgUp kPgDn kIns kDel kEnter kEsc kTab
kBack kSpace kF1..kF12 kCtrlC kNone *)
ch : CARDINAL; (* code point for kChar *)
ctrl : BOOLEAN; (* e.g. Shift-Tab gives kTab with ctrl=TRUE *)
mx, my : INTEGER; (* mouse position, 0-based *)
mbtn : INTEGER; (* 1 left, 2 middle, 3 right; held button during motion *)
mpressed, mreleased : BOOLEAN;
mwheel : INTEGER; (* -1 up, +1 down *)
END;
ReadEvent(e) : BOOLEAN waits up to ~0.1 s and returns FALSE on timeout.
HandleEvent(e) : INTEGER dispatches one event to the menu bar, the modal
dialog (if any) or the desktop, and returns a command: cmNone, cmClose,
or the tag of the activated control/menu item. Sender() : ViewPtr returns
the view that produced it (NIL for menus).
Built-in commands: cmNone cmClose cmOK cmCancel cmYes cmNo.
TYPE VKind = (VGroup, VWindow, VText, VFrame, VInput, VButton,
VCheck, VRadio, VList, VScroll);
View = RECORD ... END; ViewPtr = POINTER TO View;
| procedure | description |
|---|---|
NewView(kind, x,y,w,h) : ViewPtr |
allocate a view (absolute screen coordinates). |
AddView(parent, child) |
append a child to a group/window. |
DelView(v) |
remove a view and its children. |
SetText(v, s) / SetTag(v, n) |
set text / command tag. |
Desk() : ViewPtr |
the desktop group; add top-level windows here. |
BringToFront(v) |
raise within its parent (z-order). |
FocusView(v) / FocusNext(backwards) |
set/advance keyboard focus. |
MoveViewBy(v, dx, dy) |
move a view and its descendants. |
Fields are public: v^.rect, v^.text, v^.flags, v^.value,
v^.minv, v^.maxv, v^.tag, v^.list, v^.focus. Flags:
vfDisabled, vfSelected, vfFramed.
Widget kinds: VText (label), VFrame (box), VInput, VButton,
VCheck, VRadio, VList (needs v^.list : ListDataPtr), VScroll
(v^.value/minv/maxv), VWindow, VGroup.
TYPE ListData = RECORD
items : ARRAY [0..255] OF ARRAY [0..63] OF CHAR;
count : INTEGER;
END; ListDataPtr = POINTER TO ListData;
TYPE EditorData = RECORD ... END; EditorDataPtr = POINTER TO EditorData;
Declare one EditorData (a few tens of KB) per editor, attach it to a
VEditor view, and it becomes a full multi-line editor with its own frame
and a Ln/Col/Insert/Modified status row.
VAR ed : tv.EditorData; edp : tv.EditorDataPtr;
...
edp := CAST(tv.EditorDataPtr, ADR(ed));
tv.EditorInit(edp); (* empty; or EditorLoad below *)
tv.EditorAddLine(edp, "MODULE hello;"); (* build sample text *)
ev := tv.NewView(tv.VEditor, 2, 2, 60, 20);
tv.EditorAttach(ev, edp);
tv.AddView(win, ev);
tv.FocusView(ev);
| procedure | description |
|---|---|
EditorAttach(v, data) |
tie an editor buffer to a VEditor view |
EditorInit(data) |
clear to a single empty line |
EditorAddLine(data, s) |
append a line (for building text) |
EditorGetLine(data, i, VAR s) |
read a line back |
EditorLoad(data, path) : BOOLEAN |
load a text file into the buffer |
EditorSave(data, path) : BOOLEAN |
save the buffer to a text file |
EditorDirty(data) : BOOLEAN |
TRUE if modified since load/save |
EditorSetLanguage(data, lang) |
enable highlighting for a language |
EditorSetIndent(data, n) |
auto-indent step in spaces (default 2; 0 = off) |
Language constants: langNone, langModula2, langPascal, langOberon.
With a language set the editor highlights keywords (white), strings (red),
comments (grey; (* ... *) nestable, plus { ... } for Pascal and // for
Pascal/Modula-2) and numbers (red). For Modula-2 and Oberon, finishing a
word with a delimiter (space, Tab, Enter or punctuation) automatically
upper-cases it if it is a keyword (begin -> BEGIN).
Auto-indentation (when a language is set): Enter copies the current line's
leading spaces and adds one indent step after an open keyword (BEGIN,
THEN, ELSE, ELSIF, DO, LOOP, REPEAT, RECORD, CASE, OF);
a line whose first word is a close keyword (END, UNTIL, ELSE, ELSIF)
is dedented one step. Configure the step with EditorSetIndent.
Keys: printable text, Enter, Backspace, Delete, arrows, Home/End, PgUp/PgDn, Tab (indent to a 4-column stop), Ins (insert/overwrite). The mouse places the cursor and the wheel scrolls.
MenuBarInit, MenuAdd(title), MenuItemAdd(text, tag), MenuSep,
MenuBar(show : BOOLEAN). The menu bar is drawn on row 0; selecting an
item makes HandleEvent return its tag. Mouse: click a title, click an
item. Keyboard: F10, arrows, Enter/Space, Esc.
Build a centred modal window, add controls, then run it:
tv.Dialog("Options", 44, 12);
iName := tv.DlgInput("Name:", "Modula-2"); (* -> field index *)
cOn := tv.DlgCheck("Enabled", TRUE);
rFast := tv.DlgRadio("Fast", TRUE); (* radios auto-exclusive *)
rSafe := tv.DlgRadio("Safe", FALSE);
tv.DlgButton("OK", tv.cmOK);
tv.DlgButton("Cancel", tv.cmCancel);
cmd := tv.DlgRun; (* blocks; returns the tag *)
tv.DlgTextAt(iName, s); (* read an input *)
n := tv.DlgValue(cOn); (* 1 if selected *)
DlgRun returns the pressed button's tag, cmCancel on Esc, cmClose from
the close box.
MessageBox(title, text, kind) : INTEGER where kind is mbOK,
mbOKCancel or mbYesNo; returns cmOK, cmCancel, cmYes or cmNo.
Message(title, text) is shorthand for MessageBox(..., mbOK).
tvg renders the same view tree into a tigr window instead of a terminal.
Terminal programs do not link tigr.
IMPORT tv, tvg;
...
tvg.InitGfx("my app", 90, 30); (* title, grid cols x rows *)
... build views with tv as usual ...
tvg.Finish;
WHILE ... DO
IF tvg.ReadEvent(e) THEN cmd := tv.HandleEvent(e); ...; tvg.Finish END;
IF tvg.Closed() THEN quit := TRUE END
END;
tvg.Done
Use tv.InitVirtual(cols, rows) (not tv.Init) when driving tv from a
non-terminal backend; it sets the virtual screen size without touching the
tty. tvg maps tigr keys and mouse to the same tv.Event; the cell grid
is tv.Cols() x tv.Rows(), each cell CellW() x CellH() pixels. Text
is drawn from tvfont (a bundled 8x16 monospaced VGA font, so no system font
is required); box-drawing cells are rendered as line segments.
gm2 -fiso -c tvtty.def
gm2 -fiso -I. -c tv.mod
gm2 -fiso -I. tv.o myapp.mod -o myapp
For the graphics backend also build tvg.o (needs the tigr binding):
gm2 -fiso -I. -c tvfont.mod
gm2 -fiso -I. -I../modula2 -c tvg.mod
gm2 -fiso -I. -I../modula2 tv.o tvg.o tvfont.o \
../modula2/tigr.o ../modula2/helper.o myapp.mod -o myapp -lGL -lX11