sextile.layout

A page as furniture and parts, laid out down as many frames as it takes.

The package is three subjects, each in its own module, re-exported here so a service imports them all from sextile.layout:

  • parts: the content between the rules – Drawable and the wrappers that say which frames a part appears on, and fill, which walks them.

  • furniture: the fixed bands round the content – the header, the rules and the footer – and content_rows, the rows they leave.

  • page: PageLayout, which a service constructs and builds, and the Shortcut and home vocabulary it reads.

footer composes the prompt row and is used by both a PageLayout and a service drawing a frame by hand; its names are re-exported here too.

class sextile.layout.Claim(choices=<factory>, named=(), form=None)[source]

Bases: object

What a part claims on the frame it has drawn on.

choices

Keys that lead somewhere, which for a menu are its digits and differ from frame to frame.

named

What to name in the prompt, such as 1-9 select. The layout adds the shortcuts, the movement keys and the way home.

form

A field the reader types into, where this part is one.

Example

A part offering digit 1, named in the prompt:

Claim(choices={"1": address}, named=[FooterItem("1", "choose")])
Parameters:
merged_with(other)[source]

This claim and another, as one.

Parameters:

other (Claim) – What a later part on the same frame claimed.

Return type:

Claim

Returns:

The two merged. Where both claim the same key the later wins, which cannot arise between the digits of two flowing parts because the second is given only the choices the first left.

Raises:

ValueError – If both carry a form. A frame has one field to type into; forms.FieldSet is what composes several into one.

class sextile.layout.Custom(rows, draw)[source]

Bases: object

A part of a stated height, drawn cell by cell by the page itself.

A picture is positioned at a cell and may be several rows tall, which suits a strip of mosaics or a grid of figures and does not suit a line of text.

rows

How many rows of the frame it occupies.

draw

Called with the canvas and the row it begins on.

Example

A picture several rows tall, drawn as the whole of a page’s content:

OnOneFrame(Custom(rows=height, draw=lambda canvas, row: draw_it(canvas, row)))
Parameters:
class sextile.layout.DefaultHome[source]

Bases: object

Sentinel for a home left unset, so the app’s index stands in for it.

class sextile.layout.Drawable(*args, **kwargs)[source]

Bases: Protocol

Something drawn between the rules: a menu, some lines, a picture, a form.

A drawable is a description rather than a position, so placing one does not change it and a layout may be built more than once. The exception is a form, which holds what has been typed.

Example

A part one row tall that writes a fixed word and claims no keys:

class Word:
    def __init__(self, text: str) -> None:
        self._text = text

    def place(self, canvas: Canvas, space: Space) -> Placed:
        if space.rows < 1:
            return Placed(rows=0, remainder=self)
        canvas.row(space.first_row).text(self._text)
        return Placed(rows=1)
place(canvas, space)[source]

Draw as much as space allows, and say what is left over.

Parameters:
  • canvas (Canvas) – The frame being filled.

  • space (Space) – What the frame has left to give.

Return type:

Placed

Returns:

The rows used, what they claim, and whatever remains for the next frame. Returning nought rows and self asks for a fresh frame, which is how a drawable too tall for what is left is carried whole to the next frame rather than split.

class sextile.layout.Edge(*values)[source]

Bases: Enum

Which end of a frame a furnishing is docked to.

class sextile.layout.Flow(drawable)[source]

Bases: object

A drawable broken across as many frames as it takes.

Several may appear in one list, and they follow one another: the second begins in the row after the first has finished, on whatever frame that is.

Parameters:

drawable (Drawable)

class sextile.layout.Footer(colour=Colour.YELLOW, edge=Edge.BOTTOM, rows=1)[source]

Bases: object

Every key that works on this frame, and what each of them does.

Parameters:
class sextile.layout.FooterItem(key, label='', priority=Priority.SECONDARY, brief='')[source]

Bases: object

One key the reader may press, and what it does.

key

The character the reader presses.

label

What the prompt calls it, or empty to name the key alone.

priority

How hard the prompt tries to keep it when the row is tight.

brief

A shorter label, tried before the key sheds its label entirely.

Parameters:
wordings()[source]

Every way of writing this item, the fullest first.

Return type:

list[str]

class sextile.layout.FrameBreak[source]

Bases: object

A division the page means, rather than one the rows forced.

Whatever follows begins on a new frame. A break that would divide nothing is ignored: one at either end of the list, two together, or one on a frame with nothing yet drawn on it.

class sextile.layout.FrameContext(title, address, index, frames, offered, numbered=True)[source]

Bases: object

What a furnishing is told about the frame it is drawing on.

title

What the page is called.

address

The address the page answers to, or None where it has no number of its own to show.

index

Which frame this is, counting from nought.

frames

How many frames the page came to.

offered

Every key that works on this frame, in the order the prompt should try to name them: what the parts claimed, then the shortcuts, the movement keys, and the way home.

Parameters:
property page_number: str

The page number as this frame displays it, or empty for none.

class sextile.layout.Furnishing(*args, **kwargs)[source]

Bases: Protocol

A band docked to the top or the foot of every frame.

A furnishing claims no keys. What it names belongs to the layout or to the parts, and it is handed the assembled list rather than composing one.

property edge: Edge

Which end of the frame this is docked to.

property rows: int

How many rows it takes, on every frame.

draw(canvas, at, page)[source]

Draw this band in the rows the layout has reserved for it.

Parameters:
Return type:

None

class sextile.layout.Header(colour=Colour.CYAN, numbered=Colour.WHITE, edge=Edge.TOP, rows=1, _ATTRIBUTES=2, _GAP=1)[source]

Bases: object

The page title, and the page number at the right of the same row.

Parameters:
class sextile.layout.OnEveryFrame(drawable)[source]

Bases: object

A drawable drawn on every frame, at its place in the order.

Where it comes after any flowing part, its rows are kept back at the foot of every frame before the flowing part is placed, since a flowing part takes the rows left to it and nothing after one would be drawn at all. Where it comes before them all, it is drawn where it stands. Several of either follow one another in the order the list gives them.

Parameters:

drawable (Drawable)

class sextile.layout.OnOneFrame(drawable)[source]

Bases: object

A drawable drawn one time, at its place in the order.

On the first frame where nothing flowing comes before it, and otherwise on whichever frame the flow before it finished on.

Parameters:

drawable (Drawable)

class sextile.layout.PageLayout(*, title=None, parts=(), home=<sextile.layout.page.DefaultHome object>, numbered=True, shortcuts=(), neighbours=None, item_noun='item', furniture=(Header(colour=<Colour.CYAN: 6>, numbered=<Colour.WHITE: 7>, edge=<Edge.TOP: 1>, rows=1, _ATTRIBUTES=2, _GAP=1), Rule(edge=<Edge.TOP: 1>, colour=<Colour.BLUE: 4>, rows=1), Rule(edge=<Edge.BOTTOM: 2>, colour=<Colour.BLUE: 4>, rows=1), Footer(colour=<Colour.YELLOW: 3>, edge=<Edge.BOTTOM: 2>, rows=1)), next_page=None, hang_up=False)[source]

Bases: object

A page as its furniture and the parts laid out between it.

Construct one and call build with the request the page answers.

title

What the header calls the page. None takes the registered title of request.address, upper-cased; “” heads it with nothing.

parts

The content, in the order it appears down the frames. A bare Drawable means Flow(drawable); OnOneFrame, OnEveryFrame and FrameBreak say the frames a part appears on where they are not the default.

home

Where 0 leads from every frame. Unset takes request.app.index; None offers no way home; a PageAddress leads there under the index label; a Shortcut where the footer should call it something else, or another key should do it.

numbered

Whether the header shows the page number. False for a page with a header but no number a reader could key, such as a notice.

shortcuts

Keys offered on every frame, besides the digits and 0.

neighbours

The pages either side of this one in the sequence being read. Given, it wires A to previous and D to next (with their cursor-key arrows) wherever each is not None, and the footer names them. Pass request.neighbours; a page reached by keying its number carries a Neighbours of two Nones and offers neither.

item_noun

What A and D move between, as the footer says it.

furniture

The bands round the content. Empty for a page that wants none, such as a masthead.

next_page

Where # leads once the frames have run out. Setting it answers the next-frame keys, the session trying the next frame before falling through to this.

hang_up

Whether the line drops once the page has been shown.

Example

A menu, with a lead-in on its first frame:

PageLayout(
    title="LATEST POSTS",
    parts=[OnOneFrame(preamble), Flow(Menu(entries=posts))],
).build(request)
Parameters:
build(request)[source]

Fill the frames with the parts, then furnish them.

The title, the way home and the page number are taken from the request where the page did not give them: the registered title of request.address, request.app.index, and request.address itself.

Parameters:

request (PageRequest) – The request this page answers, supplying the address it is at and the service it belongs to.

Returns:

one frame for each the parts needed, each carrying the keys that work while it is showing.

Return type:

Page

class sextile.layout.Placed(rows, claim=Claim(choices={}, named=(), form=None), remainder=None)[source]

Bases: object

What placing a drawable on one frame came to.

rows

The rows it used, which is nought where it would not begin here.

claim

What it claims on this frame.

remainder

What is left of it for the next frame, or None where it is finished. A drawable that returns nought rows and itself is asking for a frame of its own.

Example

Two rows drawn and finished, and a part asking for a fresh frame:

Placed(rows=2)
Placed(rows=0, remainder=self)
Parameters:
class sextile.layout.Priority(*values)[source]

Bases: IntEnum

What a reader can least afford to lose, in descending order.

ESSENTIAL = 3

The way out. A reader who cannot read the screen still needs to leave it.

PRIMARY = 2

What this page is chiefly for.

SECONDARY = 1

Moving about.

ALIAS = 0

An alias for something already shown, such as # beside S.

class sextile.layout.Rule(edge=Edge.TOP, colour=Colour.BLUE, rows=1)[source]

Bases: object

A rule across the middle of a row, dividing the content from the rest.

Parameters:
class sextile.layout.Shortcut(key, destination, label='', with_arrow=False, priority=Priority.PRIMARY)[source]

Bases: object

A key offered on every frame of a page, always leading to one address.

key

The character the reader presses, such as * or R.

destination

The address that key leads to, from every frame.

label

How the footer names the key. Put the short form first: the footer sheds words from the end when a row is tight, so “index, or key another page” degrades to “index” and then to the bare key.

with_arrow

Whether the matching cursor key leads there as well. Only W, A, S and D have one; asking on any other key adds nothing rather than raising.

priority

How hard the footer tries to keep it. A key that is the point of the page outranks one that is a convenience.

A page’s digits belong to its entries and change from frame to frame, but a shortcut is fixed. It is for the way out that is not the way home, such as a page returning to the one that led to it.

Parameters:
class sextile.layout.Space(first_row, rows, choices)[source]

Bases: object

What is left of a frame when a part is asked to draw on it.

first_row

The row the part would begin on.

rows

How many rows are left, which may be nought.

choices

How many of the digits 1-9 are still unclaimed on this frame. A reader chooses with one keypress, so this is a budget the whole frame shares however it is divided between parts.

Example

Twelve rows from row 6, with all nine digits still free:

Space(first_row=6, rows=12, choices=9)
Parameters:
sextile.layout.content_rows(furniture)[source]

Which rows of a frame are left for the content.

Parameters:

furniture (Sequence[Furnishing]) – The bands docked to the frame, in the order they are drawn down it.

Return type:

range

Returns:

The rows between them, which is the whole frame where there is no furniture at all.

sextile.layout.movement(available, *, item='item')[source]

Build the footer items for the movement keys a frame answers, in order.

Parameters:
  • available (Iterable[str]) – The movement keys the frame answers, as their letters.

  • item (str) – The noun A and D move between, as the prompt names it.

Return type:

list[FooterItem]

Returns:

A FooterItem for each movement key answered, in a fixed order.

Here so a PageLayout and a frame drawn by hand name these keys the one way, rather than two pages of a service describing the same key differently.

Compose the prompt, shedding what will not fit in priority order.

Parameters:
  • items (Sequence[FooterItem]) – The keys to name, in the order the prompt should try to keep them, most worth saying last off.

  • width (int) – The cells the row has, FOOTER_WIDTH for a full-width frame.

Return type:

str

Returns:

The prompt, shortened to width by shedding labels and then keys.