sextile.viewdata.canvas

Drawing on a frame, with the cost of attributes accounted for.

A spacing attribute occupies a character cell, so a row that changes colour twice has thirty-eight columns left for text rather than forty. Every method here works in cells, not in characters, so a caller never has to remember that.

Attributes reset at the start of each row on the SAA5050, so a row is written independently of its neighbours and white text needs no attribute at all. Rows are obtained one at a time from Canvas.row for exactly that reason.

class sextile.viewdata.canvas.Canvas(frame=None)[source]

Bases: object

A frame under construction, drawn on a row at a time.

Parameters:

frame (Frame | None)

row(row)[source]

A writer positioned at the start of a row.

Parameters:

row (int)

Return type:

RowWriter

place(tile, *, row, column)[source]

Place a tile’s pre-solved cells on the frame, as they stand.

Each cell goes down unchanged – an attribute as an attribute, a character as a character – with nothing inserted and nothing charged: the tile’s solver has already spent its attribute cells. Where text is written after a tile on the same row, a RowWriter.starting_at that column reads the tile’s own attributes and escapes them.

Parameters:
  • tile (Tile) – The rectangle of cells to place.

  • row (int) – The top row it begins on.

  • column (int) – The column its left edge begins at.

Raises:

ValueError – If any of the tile would fall outside the frame, naming the overflow rather than clipping it.

Return type:

None

right(row, text, colour=None)[source]

Write text ending at the right edge of the row.

Parameters:
Return type:

RowWriter

class sextile.viewdata.canvas.RowWriter(frame, row)[source]

Bases: object

A cursor along one row, tracking the colour and mode in effect.

Parameters:
property column: int

The next free column.

property remaining: int

Cells left in the row.

text(text, colour=None)[source]

Append text, preceded by a colour attribute if one is needed.

One is needed if the colour changes, and also if the row is in graphics: the attribute that returns to alpha is the same attribute that sets the colour, so leaving a mosaic run costs a cell whether the colour changes or not.

Parameters:
Return type:

Self

runs(runs, *, cells=None)[source]

Append several stretches of text, each in its own colour.

What text does repeatedly, with the difference that this trims rather than raises: a line assembled from runs is usually a line assembled from data, and a value longer than anybody expected should cost the reader the end of a line rather than the whole frame.

A cell is held back from each run for the attribute that may precede it, which costs nothing except in the rare case where the trimming actually bites – and there one character is a cheap price for not having to ask text what it is about to charge.

Parameters:
  • runs (Iterable[Span]) – The stretches to write, in order.

  • cells (int | None) – A budget the runs share, giving way within it rather than at the row’s edge. For a row that carries something further along it – a clock, then a figure aligned right of it – so the first does not eat the room the second needs. The row’s edge still binds when it is the nearer of the two.

Return type:

Self

mosaic(patterns, colour, *, separated=False)[source]

Append mosaic cells, preceded by whatever attributes they need.

Each pattern is six bits, one per block, in the order mosaic_code names them.

Two attributes may be wanted and they do different things. The separated attribute chooses which graphics set is selected, and takes effect whether or not graphics are in force; the colour attribute is what enters graphics, and carries the colour with it. So a contiguous run in a colour already in force costs one cell, a separated one costs two, and staying in the same run costs nothing.

Attributes display as spaces, which is why a region drawn this way has a margin on its left whether it wants one or not. That cost is knowable and has to be planned around – it is why the rules this service draws begin at column 2 – and HOLD_GRAPHICS is the way out of it where a gap would show, since it makes an attribute cell repeat the last mosaic instead of blanking.

Parameters:
Return type:

Self

skip(cells)[source]

Advance without writing, leaving the cells blank.

Whatever colour is in force where the cursor lands becomes this writer’s colour. Skipping is how a second writer reaches a place further along a row somebody else has already written, and assuming white there would emit no attribute and leave the text silently taking the earlier colour.

Parameters:

cells (int)

Return type:

Self

background(colour, *, text)[source]

Colour the rest of this row’s background, and what is written on it.

Parameters:
  • colour (Colour) – The background colour, which runs to the end of the row unless end_background stops it.

  • text (Colour) – The colour of what is written on it.

Return type:

Self

Three cells, the hardware’s arrangement rather than a choice: a background can only be taken from a foreground, so the colour is chosen, made the background, and the text colour chosen again. The bar of colour a reader can see the extent of is what makes a field look like a field.

end_background()[source]

End any background, so what follows sits on black again.

One cell. The foreground is untouched – black is taken as a background directly, being the one colour that needs no foreground chosen first.

What this is for is bounding a field. A background runs to the end of the row unless something stops it, which says “type as much as you like”; a field of known width should say how much room there is, which means saying where the room ends.

Return type:

Self

property colour: Colour

The colour the next character would take.

starting_at(column)[source]

Move to a column and pick up the colour and mode in force there.

For a second writer beginning part way along a row another has already drawn on – a legend’s words beside a symbol, a figure after a picture. Reading what is in force is what makes the next write escape it: white text after a mosaic emits the attribute that returns to letters, rather than coming out as blocks. The column must not be behind the cursor.

Parameters:

column (int)

Return type:

Self

class sextile.viewdata.canvas.Span(text, colour=None)[source]

Bases: object

A stretch of text in one colour.

text

The characters to write.

colour

The colour to write them in, or None to keep the one in force.

For RowWriter.runs, a line whose colours carry meaning rather than decoration: several values side by side, told apart by colour rather than by a label that would cost cells to repeat what the row above already said. A row in one colour needs no Span; RowWriter.text takes the colour with the words.

Parameters: