Skip to content

xnano.context

xnano.context

xnano.context

What a hook is called with, and one frame or update of the clock.

Classes:

  • Plugin –

    The attachment of a hook: the hook or component that is running right now.

  • ReadLog –

    What a component read while it was evaluated: the record that decides when it runs again.

  • Context –

    What a hook is called with.

  • Frame –

    One frame of a canvas's clock.

  • Update –

    One step of a simulation, which runs separately from drawing.

Plugin

Plugin(hook: HookHandle[object, object])

The attachment of a hook: the hook or component that is running right now.

context.plugin names that one attachment and nothing else. The hook itself is context.hook.

Attributes:

  • hook –

    The hook this attachment belongs to.

  • state (StateNamespace) –

    State that belongs to this attachment alone, read with get and written with push; see StateNamespace.

Create the attachment of a hook or component. Use context.plugin instead of calling this.

Parameters:

Source code in xnano/context.py
def __init__(self, hook: HookHandle[object, object]) -> None:
    """
    Create the attachment of a hook or component. Use ``context.plugin`` instead of
    calling this.

    Args:
        hook: The hook or component driver that is running.
    """
    self.hook = hook

state property

State that belongs to this hook alone.

No other hook or placement sees it, and it is deleted when the hook is removed. To share a value between hooks, keep it in context.root.state.

Examples:

import xnano
from xnano_core import Event

with xnano.new(width=8, height=1) as canvas:

    @canvas.hook(on='key')
    def count(context: xnano.Context) -> None:
        state = context.plugin.state
        state.push('presses', state.get('presses', default=0) + 1)
        context.root.state.push('latest', state.get('presses'))

    canvas.push(Event.key_press('a'))
    canvas.push(Event.key_press('b'))
    canvas.deliver_events()
    assert canvas.state.get('latest') == 2

ReadLog

ReadLog()

What a component read while it was evaluated: the record that decides when it runs again.

A component that read nothing that changes is evaluated once. One that read the time is evaluated again on every frame (or every beat for a playable), one that read the size when the screen is resized, and one that read an input when that input arrives.

Attributes:

  • uses_time –

    Whether it read the clock, the event or the held keys.

  • uses_size –

    Whether it read the size of the canvas or of a buffer.

  • uses_deps –

    Whether it read the dependencies.

  • input_kinds (set[str]) –

    The kinds of input it read: 'key', 'mouse', 'scroll' or 'paste'.

Start an empty record.

Source code in xnano/context.py
def __init__(self) -> None:
    """Start an empty record."""
    self.uses_time = False
    self.uses_size = False
    self.uses_deps = False
    self.input_kinds: set[str] = set()

is_dynamic property

is_dynamic: bool

Whether anything that changes was read (the dependencies never change).

Context

Context(canvas: Canvas[DepsT_co, StateT_co], owner: Canvas[object, object] | Placement[object, object], hook: HookHandle[object, object], event: Event | TerminalStateChange | Frame | Update | None, core: CoreContext | None, log: ReadLog | None = None)

Bases: Generic[DepsT_co, StateT_co]

What a hook is called with.

The context carries the event and live views of where the hook is attached. Its members are read when you use them, so a context costs almost nothing to receive. A generator hook receives a new context each time it resumes, so context.mouse.position is always the newest event's.

buffer, parent and root are live objects with concrete values: context.parent.width is an int, and context.buffer has the verbs of a placement (move, up, down, left and right). On a placement or canvas themselves, parent and root are formula scopes whose members are expressions, such as placement.parent.width.

keyboard, mouse, scroll and clipboard are always there; each member is None (or 0 for the wheel) unless the event the hook is handling is of that kind. Each has get() and push(value).

Attributes:

Examples:

import xnano
from xnano_core import Event

with xnano.new(width=20, height=3) as canvas:
    label = canvas.write('hi', x=2)
    heard = []

    @label.hook(on='mouse', when="mouse.kind == 'press'")
    def pressed(context: xnano.Context) -> None:
        heard.append((context.mouse.position.x, context.buffer.x))

    canvas.push(Event.mouse_event(column=3, row=0, kind='press', button='left'))
    canvas.deliver_events()
    assert heard == [(3, 2)]

Create a context. The runtime does this for you; hooks receive one.

Parameters:

  • canvas (Canvas[DepsT_co, StateT_co]) –

    The root canvas.

  • owner (Canvas[object, object] | Placement[object, object]) –

    The canvas or placement the hook is attached to.

  • hook (HookHandle[object, object]) –

    The hook that is called.

  • event (Event | TerminalStateChange | Frame | Update | None) –

    The event, or None.

  • core (CoreContext | None) –

    The runtime's context for the event, or None.

  • log (ReadLog | None, default: None ) –

    Where to record what is read, for a component; None for a hook.

Methods:

  • local –

    Convert a cell of the screen into a cell of the hook's buffer.

Source code in xnano/context.py
def __init__(
    self,
    canvas: Canvas[DepsT_co, StateT_co],
    owner: Canvas[object, object] | Placement[object, object],
    hook: HookHandle[object, object],
    event: _core.Event | _core.TerminalStateChange | Frame | Update | None,
    core: _core.CoreContext | None,
    log: ReadLog | None = None,
) -> None:
    """
    Create a context. The runtime does this for you; hooks receive one.

    Args:
        canvas: The root canvas.
        owner: The canvas or placement the hook is attached to.
        hook: The hook that is called.
        event: The event, or ``None``.
        core: The runtime's context for the event, or ``None``.
        log: Where to record what is read, for a component; ``None`` for a hook.
    """
    self.canvas = canvas
    self._owner = owner
    self.hook = hook
    self._event = event
    self._core = core
    self._log = log

canvas instance-attribute

canvas: Canvas[DepsT_co, StateT_co] = canvas

The root canvas the hook belongs to.

hook instance-attribute

hook: HookHandle[object, object] = hook

The registration, so the hook can call hook.remove().

event property

event: Event | TerminalStateChange | Frame | Update | None

The event: a core event for input, a Frame for hook(on='frame'), an Update for hook(on='tick'), a state change for hook(on='state'), or None when no event caused the call.

keyboard property

keyboard: KeyboardInput

The key of the event; key is None unless the event is a key.

mouse property

mouse: MouseInput

The mouse button or movement of the event; position is None for any other.

scroll property

scroll: ScrollInput

The mouse wheel steps of the event; x and y are 0 unless it is a wheel.

clipboard property

clipboard: ClipboardInput

The pasted text of the event; text is None unless the event is a paste.

buffer property

The placement the hook is attached to, or the canvas for a hook on the canvas.

parent property

What contains the hook's placement: another placement, or the canvas.

root property

root: Canvas[DepsT_co, StateT_co]

The root canvas.

plugin property

plugin: Plugin

The attachment of the hook that is running.

deps property

deps: DepsT_co

The dependencies given to new(deps=...), or None.

fx property

Applies effects to the hook's buffer while the hook is active.

time property

time: Time

The canvas's Time.

audio property

audio: AudioDevice

The canvas's AudioDevice.

cursor property

cursor: Cursor

The canvas's cursor.

size property

size: Size

The canvas's size in cells.

input property

input: Input

The keys and mouse buttons held down right now.

local

local(point: Point) -> Point

Convert a cell of the screen into a cell of the hook's buffer.

Parameters:

  • point (Point) –

    A cell in screen coordinates, such as context.mouse.position.

Examples:

import xnano
from xnano_core import Point

with xnano.new(width=20, height=4) as canvas:
    placement = canvas.write('hi', x=10, y=1)

    @placement.hook(on='mouse')
    def pressed(context: xnano.Context) -> None:
        if context.mouse.position is not None:
            context.local(context.mouse.position)

    assert placement.local(Point(11, 1)) == Point(1, 0)
Source code in xnano/context.py
def local(self, point: _core.Point) -> _core.Point:
    """
    Convert a cell of the screen into a cell of the hook's buffer.

    Args:
        point: A cell in screen coordinates, such as ``context.mouse.position``.

    Examples:
        ```python
        import xnano
        from xnano_core import Point

        with xnano.new(width=20, height=4) as canvas:
            placement = canvas.write('hi', x=10, y=1)

            @placement.hook(on='mouse')
            def pressed(context: xnano.Context) -> None:
                if context.mouse.position is not None:
                    context.local(context.mouse.position)

            assert placement.local(Point(11, 1)) == Point(1, 0)
        ```
    """
    point = check_point(point)
    bounds = self._owner._buffer._absolute_bounds
    return _core.Point(point.x - bounds.x, point.y - bounds.y)

Frame dataclass

Frame(canvas: Canvas[object, object], index: int, elapsed_seconds: float, delta_seconds: float, area: Rectangle)

One frame of a canvas's clock.

Attributes:

canvas instance-attribute

canvas: Canvas[object, object]

The canvas the frame belongs to.

index instance-attribute

index: int

The number of steps the canvas's clock has counted, which is Time.frame when the frame was made.

elapsed_seconds instance-attribute

elapsed_seconds: float

Seconds on the canvas's clock.

delta_seconds instance-attribute

delta_seconds: float

Seconds since the previous frame (0.0 for the first).

area instance-attribute

area: Rectangle

The rectangle the frame is drawn into, in cells.

Update dataclass

Update(canvas: Canvas[object, object], elapsed_seconds: float, delta_seconds: float, is_fixed_step: bool)

One step of a simulation, which runs separately from drawing.

Attributes:

canvas instance-attribute

canvas: Canvas[object, object]

The canvas the update belongs to.

elapsed_seconds instance-attribute

elapsed_seconds: float

Seconds on the update's clock.

delta_seconds instance-attribute

delta_seconds: float

Seconds this update advances.

is_fixed_step instance-attribute

is_fixed_step: bool

Whether the update runs at a fixed rate.