class

TermBuf::Grid

Inherits Reference < Object

Stability: internal

A rectangular array of cells.

Cells live in one flat slice, so a row is a contiguous Slice view and scrolling is a run of memmoves. Alongside them the grid keeps a hash per row, recomputed lazily, which is what lets the painter recognise a scrolled band without comparing cells.

The grid is the only place that knows a cluster can occupy more than one cell, and it never lets those cells get out of step: writing to any of them blanks all of them first.

Constants

FNV_OFFSET = 14695981039346656037_u64
FNV_PRIME = 1099511628211_u64

Constructors

new(width : Int32, height : Int32, blank : Cell = Cell.blank)
Source

Instance methods

==(other : Grid) : Bool

Whether both grids hold the same cells. Damage is not compared: it says what has yet to be painted, not what is on the screen.

Source
[](x : Int32, y : Int32) : Cell

The cell at (x, y). Raises if it is off the grid.

Source
[]=(x : Int32, y : Int32, cell : Cell) : Nil

Writes one cell without regard for wide character pairing. Callers that place characters want #place; this is for filling, scrolling, and the internals of #place itself.

Source
[]?(x : Int32, y : Int32) : Cell | Nil

The cell at (x, y), or nil if it is off the grid.

Source
bounds

The rectangle covering every cell.

Source
clear(cell : Cell = Cell.blank) : Nil

Sets every cell of the grid to cell.

Source
clip_wide(rect : Rect, blank : Cell = Cell.blank) : Nil

Blanks any cluster straddling the left or right edge of rect, so that filling or scrolling the rectangle cannot tear one apart.

The cells lying outside rect keep the style they had and lose only their share of the glyph. A rectangle operation has no business changing how a cell outside it looks, and giving those cells the incoming style would paint the rectangle wider than it is — on the rows where a cluster happens to straddle, and not on the others.

Source
contains?(x : Int32, y : Int32) : Bool

Whether (x, y) is on the grid.

Source
copy_from(other : Grid) : Nil

Replaces this grid's contents with other's, which must be the same size. Used to bring the front buffer up to date once a paint has been written to the terminal.

Source
damage

Which rows have changed, and over what span.

Source
detach(x : Int32, y : Int32, blank : Cell = Cell.blank) : Nil

Blanks every cell of the cluster overlapping column x, if one covers more than that column. A cell that leads nothing is left alone.

Every cell of it takes blank, which for #place is the style being written: a terminal erases what it displaces in whatever the current style is, having no memory of what the cell used to be, and this follows it. The rectangle operations want a different answer and use #clip_wide.

Source
extent(x : Int32, y : Int32) : Range(Int32, Int32)

The columns the cluster covering (x, y) occupies. A cell that leads nothing covers itself alone, and so does a continuation at the left edge with no lead left to walk to.

Source
fill(rect : Rect, cell : Cell) : Nil

Sets every cell of rect to cell.

Source
height

Rows down.

Source
lead_of(x : Int32, y : Int32) : Int32

The column the cluster covering (x, y) begins at, which is x itself unless that cell is a continuation. Off the grid, x.

Source
place(x : Int32, y : Int32, cell : Cell, blank : Cell = Cell.blank) : Int32

Places cell at (x, y), blanking every cell of any cluster it displaces. Returns the columns consumed, or zero when the cluster will not fit before the right edge and nothing was written.

Source
resize(width : Int32, height : Int32, blank : Cell = Cell.blank) : Nil

Resizes the grid, keeping whatever content still fits anchored at the top left and filling the rest with blank. Every cell is left marked dirty: the terminal's own reflow is not something the buffer can predict, so the next paint has to be a full one.

Source
row(y : Int32) : Slice(Cell)

The cells of row y, as a view into the grid's own storage.

Source
row_hash(y : Int32) : UInt64

FNV-1a hash of row y, recomputed only when the row has been written to since it was last asked for.

Source
row_span(rect : Rect, y : Int32) : Slice(Cell)

The cells of row y within rect's columns.

Source
scroll(rect : Rect, lines : Int32, blank : Cell = Cell.blank, & : Slice(Cell) -> ) : Nil

Moves the contents of rect by lines rows, positive scrolling up so that content moves toward the top and blank rows appear at the bottom.

Each row that leaves the rectangle is yielded before being overwritten. The yielded slice is a view into the grid, valid only until the block returns, so a consumer keeping the row must copy it.

Source
scroll(rect : Rect, lines : Int32, blank : Cell = Cell.blank) : Nil

Scrolls without inspecting the rows that leave.

Source
to_text(pool : ClusterPool = ClusterPool.new) : String

Renders the grid as text, one line per row, for specs and debugging. Continuation cells contribute nothing, so a wide character appears once.

Source
unwatch(damage : Damage) : Nil

Stops marking damage.

Source
watch(damage : Damage) : Nil

Starts marking damage alongside the grid's own on every write.

One Sink is one watcher: each output painting this grid needs its own record of what it has yet to draw, because they paint at different times and one committing must not tell another its rows are clean. There are one or two of these, so marking them is a loop over a short array rather than anything cleverer.

Source
width

Columns across.

Source