The character grid: a fixed cols by rows rectangle of cells.
A row is a list of segments, each covering a span of columns:
{:text, span, binary, style} span single-width graphemes sharing one style
{:wide, grapheme, style} one double-width grapheme, spanning two columnsThe spans of a row always sum to exactly cols. There is no unwritten column: a
blank is a space, and a blank row is a single segment.
Writing splits the row at the target columns, drops the columns being replaced, and
splices the new segments in. Splitting inside a :wide segment yields a blank on
each side, so overwriting either half of a double-width grapheme blanks the other.
Adjacent segments sharing a style are coalesced when the incoming segment spans
eight columns or fewer.
Rows are held in a map keyed by an absolute index. origin is the absolute index of
the top visible row: scrolling the whole screen changes origin and drops the rows
that left, while a scroll region narrower than the screen shuffles its own rows.
Row and column indices are zero-based screen positions; origin appears in no
argument or return value. Scroll regions are given as top inclusive and bottom
exclusive.
Summary
Functions
Remove cells at col, pulling the rest of the row left.
Remove rows at row, pulling the rest of the region up.
Blank cells in place, without shifting the rest of the row.
Blank part or all of the screen, relative to a cursor position.
Blank part or all of a row.
The cell at a position, or a blank cell when the position is off-grid.
Open blank cells at col, pushing the rest of the row right.
Open blank rows at row, pushing the rest of the region down.
A stored line as {text, style} runs, for rows held outside the grid.
A blank grid of the given size.
Write a single cell.
Write a run of graphemes starting at col, given as {grapheme, width} pairs.
Write a run of single-width printable ASCII starting at col.
Resize the grid.
The cells of a row as a dense list, cols long.
A row as a list of {text, style} runs, left to right.
The text of a row, with spacers omitted and blanks rendered as spaces.
Scroll a region down, discarding rows pushed off the bottom of it.
Scroll a region up, returning the grid and the rows that left the top of it.
Every row's text, top to bottom.
Types
@type erase_mode() :: :to_end | :to_start | :all
@type line() :: [segment()]
@type segment() :: {:text, pos_integer(), binary(), ETee.Style.t()} | {:wide, String.t(), ETee.Style.t()}
@type t() :: %ETee.Grid{ blank_row: line(), cols: non_neg_integer(), lines: %{required(integer()) => line()}, origin: integer(), rows: non_neg_integer() }
Functions
@spec delete_chars(t(), integer(), integer(), pos_integer(), ETee.Style.t()) :: t()
Remove cells at col, pulling the rest of the row left.
@spec delete_lines( t(), integer(), integer(), integer(), pos_integer(), ETee.Style.t() ) :: t()
Remove rows at row, pulling the rest of the region up.
@spec erase_chars(t(), integer(), integer(), pos_integer(), ETee.Style.t()) :: t()
Blank cells in place, without shifting the rest of the row.
@spec erase_display(t(), integer(), integer(), erase_mode(), ETee.Style.t()) :: t()
Blank part or all of the screen, relative to a cursor position.
@spec erase_line(t(), integer(), integer(), erase_mode(), ETee.Style.t()) :: t()
Blank part or all of a row.
@spec get(t(), integer(), integer()) :: ETee.Cell.t()
The cell at a position, or a blank cell when the position is off-grid.
@spec insert_chars(t(), integer(), integer(), pos_integer(), ETee.Style.t()) :: t()
Open blank cells at col, pushing the rest of the row right.
@spec insert_lines( t(), integer(), integer(), integer(), pos_integer(), ETee.Style.t() ) :: t()
Open blank rows at row, pushing the rest of the region down.
@spec line_runs(line()) :: [{String.t(), ETee.Style.t()}]
A stored line as {text, style} runs, for rows held outside the grid.
@spec new(pos_integer(), pos_integer()) :: t()
A blank grid of the given size.
@spec put(t(), integer(), integer(), ETee.Cell.t()) :: t()
Write a single cell.
A double-width grapheme spans two columns; one written at the last column becomes a blank instead.
Write a run of graphemes starting at col, given as {grapheme, width} pairs.
Runs of single-width graphemes coalesce into one segment; each double-width grapheme
becomes its own. The caller must ensure col plus the summed widths fits within
cols.
Write a run of single-width printable ASCII starting at col.
The binary is stored as given, without copying. The caller must ensure the bytes are
printable ASCII and that col plus the run length fits within cols.
@spec resize(t(), pos_integer(), pos_integer()) :: t()
Resize the grid.
Growing pads with blank rows at the bottom and blank columns at the right. Shrinking drops rows from the top and columns from the right.
@spec row_cells(t(), integer()) :: [ETee.Cell.t()]
The cells of a row as a dense list, cols long.
@spec row_runs(t(), integer()) :: [{String.t(), ETee.Style.t()}]
A row as a list of {text, style} runs, left to right.
Runs cover the full width of the row, blanks included. A double-width grapheme is returned as a run of its own whose text is that single grapheme.
The text of a row, with spacers omitted and blanks rendered as spaces.
@spec scroll_down(t(), integer(), integer(), pos_integer(), ETee.Style.t()) :: t()
Scroll a region down, discarding rows pushed off the bottom of it.
@spec scroll_up(t(), integer(), integer(), pos_integer(), ETee.Style.t()) :: {t(), [line()]}
Scroll a region up, returning the grid and the rows that left the top of it.
Evicted rows are returned oldest first. Scrolling by more than the region height returns at most one region's worth.
Every row's text, top to bottom.