Terminal UI

PXX includes a small terminal UI stack for ANSI terminals. It is split into low-level escape helpers, a buffered screen manager, and pure state helpers for common widgets.

These units are intended for local terminal programs. They are not a replacement for a full GUI toolkit, and they assume an ANSI-compatible terminal.

Units

Unit Purpose
ansiterm ANSI escape strings, cursor movement, raw mode, terminal size, and unbuffered terminal output.
screen Buffered ncurses-style screen manager with colors, clipping, boxes, lines, key decoding, and minimal repaint output.
lineedit Pure single-line editor state: text, cursor, insertion, deletion, and navigation keys.
menu Pure vertical menu navigation helper. The caller renders the selected item.
ansirender Render image buffers as ASCII, 256-color ANSI, or true-color block output.

Screen Drawing

The screen unit keeps a back buffer and a front buffer. Drawing calls change the back buffer. ScreenRender computes the escape sequence needed to update the terminal, and ScreenRefresh writes that sequence to stdout.

For non-interactive layout tests, initialize a fixed-size screen and inspect the plain text rows:

program screen_layout;

uses screen;

begin
  ScreenInitSize(20, 5);
  ScreenBox(0, 0, 20, 5);
  ScreenWrite(2, 2, 'Hello');
  writeln(ScreenDumpRow(0));
  writeln(ScreenDumpRow(2));
end.

Expected output:

+------------------+
| Hello            |

For a real full-screen TUI, enter raw alternate-screen mode with ScreenStart, redraw the back buffer each frame, call ScreenRefresh, then restore the terminal with ScreenEnd:

program fullscreen_example;

uses screen;

begin
  ScreenStart;
  try
    ScreenClear;
    ScreenWrite(2, 1, 'Press q to quit');
    ScreenRefresh;
    while ScreenWaitKey <> Ord('q') do begin end;
  finally
    ScreenEnd;
  end;
end.

If you avoid exceptions in a small demo, use the same shape with a Boolean loop and make sure ScreenEnd runs before the program exits.

Colors And Attributes

ScreenSetPen(fg, bg, attr) selects the current drawing style.

Common color constants:

Constant Meaning
COLOR_DEFAULT Use the terminal default color.
COLOR_BLACK through COLOR_WHITE Normal ANSI colors 0..7.
COLOR_BRIGHT_BLACK through COLOR_BRIGHT_WHITE Bright ANSI colors 8..15.

Attributes can be combined with bitwise or:

program color_example;

uses screen;

begin
  ScreenInitSize(10, 1);
  ScreenSetPen(COLOR_BRIGHT_YELLOW, COLOR_DEFAULT, ATTR_BOLD or ATTR_UNDERLINE);
  ScreenWrite(0, 0, 'Warning');
  writeln(ScreenDumpRow(0));
end.

Available attributes are ATTR_NONE, ATTR_BOLD, ATTR_DIM, ATTR_UNDERLINE, and ATTR_REVERSE.

Clipping

ScreenSetClip(x, y, w, h) makes later draw coordinates relative to a rectangular region. This is the basis for panels:

program clipping_example;

uses screen;

begin
  ScreenInitSize(13, 5);
  ScreenSetClip(0, 1, 6, 4);
  ScreenBox(0, 0, 6, 4);
  ScreenWrite(1, 1, 'L');

  ScreenSetClip(7, 1, 6, 4);
  ScreenBox(0, 0, 6, 4);
  ScreenWrite(1, 1, 'R');

  ScreenResetClip;
  writeln(ScreenDumpRow(2));
end.

Keyboard Input

Plain byte keys are returned as their ordinal values. Non-byte keys use KEY_* constants from screen:

Key Constant
Arrow keys KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT
Home / End KEY_HOME, KEY_END
Page keys KEY_PGUP, KEY_PGDN
Insert / Delete KEY_INS, KEY_DEL
Escape KEY_ESC
No key KEY_NONE
Unknown escape sequence KEY_UNKNOWN

Use ScreenWaitKey in an event loop when you want to block until input arrives. Use ScreenReadKey for polling.

ScreenDecodeKey is pure and useful in tests:

program key_example;

uses screen;

begin
  if ScreenDecodeKey(#27 + '[A') = KEY_UP then
    writeln('up');
end.

Line Editing

lineedit stores only text and cursor state. It does not draw itself, so it can be tested without a terminal and rendered however the application wants.

program lineedit_example;

uses screen, lineedit;

var
  edit: TLineEdit;

begin
  LineEditInit(edit);
  LineEditKey(edit, Ord('h'));
  LineEditKey(edit, Ord('i'));
  LineEditKey(edit, KEY_LEFT);
  LineEditKey(edit, Ord('!'));
  writeln(edit.Text);
end.

LineEditKey returns True when it consumed a printable character or editing key. It returns False for keys such as Enter or Escape so the caller can submit or cancel.

menu provides MenuNavigate(count, selected, key, wrap). It updates the selected index for arrow, Home, and End keys, clamps out-of-range selections, and optionally wraps at the ends.

Rendering is caller-side:

program menu_render_example;

uses screen, menu;

var
  items: array[0..2] of AnsiString;
  i, selected: Integer;

begin
  items[0] := 'Open';
  items[1] := 'Save';
  items[2] := 'Quit';
  selected := MenuNavigate(3, 0, KEY_DOWN, True);

  ScreenInitSize(8, 3);
  for i := 0 to 2 do
  begin
    if i = selected then
      ScreenSetPen(COLOR_DEFAULT, COLOR_DEFAULT, ATTR_REVERSE)
    else
      ScreenSetPen(COLOR_DEFAULT, COLOR_DEFAULT, ATTR_NONE);
    ScreenWrite(1, i, items[i]);
  end;
  writeln(ScreenDumpRow(selected));
end.

See examples/tui/menudemo.pas for a complete interactive menu.

Image Rendering

ansirender converts an image.TImage to terminal text:

Function Output
RenderAscii Plain ASCII luminance ramp.
RenderAnsi256 256-color ANSI foreground output.
RenderAnsiTrueColorHalfBlock True-color upper-half-block cells.
RenderAnsiTrueColorQuadrant True-color quadrant-block approximation.

These functions return strings; they do not write to the terminal directly.

Next