Skip to content
docsv0.3.0

FocusableInterface

A node renderer that paints a text caret (a single-cell cursor a user can move inside a field) implements this contract so the TUI loop can position the hardware terminal cursor at that cell for IME candidate-window placement. The TUI analog of pi-tui's `Focusable` interface. The renderer emits a zero-width marker ({@see CURSOR_MARKER}) inside its rendered output at the column where the caret lives; the loop scans the painted buffer for that marker and emits a cursor-position escape (`\x1b[{row};{col}H`) pointing the hardware cursor there. This keeps the "where is the caret" answer in the renderer (which owns the layout) while keeping the "tell the terminal" answer in the loop (which owns the output stream) — neither side has to mirror the other's concerns. Node renderers that do NOT show a text caret (buttons, lists, badges, spacers, …) do not implement this; their focus state is purely visual (the `›` marker, the theme's `selected` role) and the hardware cursor stays hidden while they are focused.

FocusableInterface::hasCaret()

abstract public function hasCaret(string $output): bool

Whether this renderer is currently painting a caret in `$output`. Implementations should answer true only when their node is focused and a text caret is actually drawn — not when the node is merely present or selected. The loop uses this to skip the marker scan when no caret exists, and to decide whether to show or hide the hardware cursor.

Parameters

Parameters of hasCaret()
NameTypeDescription
$outputstring

FocusableInterface::caretPosition()

abstract public function caretPosition(string $output): ?array

Returns the `[row, col]` zero-indexed cell of the caret in `$output`, or `null` if no marker is present. The loop adds the node's layout bounds to these coordinates to get the absolute terminal cell the hardware cursor should move to.

Parameters

Parameters of caretPosition()
NameTypeDescription
$outputstring