Skip to content

xnano.events

xnano.events

xnano.events


Inspect keyboard, mouse, resize, focus, and clipboard events.

Classes:

Functions:

Attributes:

EventDataType module-attribute

EventDataType: TypeAlias = Literal[
    "keyboard",
    "mouse",
    "resize",
    "clipboard",
    "focus",
    "tick",
    "other",
]

Public event payload category.

KeyboardEventKind module-attribute

KeyboardEventKind: TypeAlias = Literal[
    "press", "release", "repeat"
]

Keyboard transition reported by a runtime.

MouseEventKind module-attribute

MouseEventKind: TypeAlias = Literal[
    "press",
    "release",
    "drag",
    "move",
    "scroll_up",
    "scroll_down",
    "scroll_left",
    "scroll_right",
]

Mouse movement, button, or wheel transition.

FocusEventKind module-attribute

FocusEventKind: TypeAlias = Literal[
    "gained", "lost", "field_gained", "field_lost"
]

Terminal or field focus transition.

EventData module-attribute

Payload carried by a public Event.

AbstractEventData dataclass

AbstractEventData()

Base for event payloads.

Attributes:

type class-attribute

type: EventDataType = 'other'

Payload category.

KeyboardEventData dataclass

KeyboardEventData(
    _native_event: object | None = None,
    _kind: KeyboardEventKind = "press",
    _binding: str | None = None,
    _character: str | None = None,
)

Bases: AbstractEventData

Keyboard input and its normalized binding.

Attributes:

Examples:

event = KeyboardEventData.from_binding("ctrl+s")
if event.matches("ctrl+s"):
    save()

Methods:

  • from_binding

    Create keyboard data from a binding string.

  • matches

    Return whether this event matches any binding.

type class-attribute

type: Literal['keyboard'] = 'keyboard'

Payload category.

binding property

binding: KeyboardBinding

Normalized binding represented by this event.

key property

key: KeyboardKey | str | None

Primary key represented by this event.

kind property

Keyboard transition represented by this event.

modifiers property

modifiers: list[KeyboardModifier]

Modifier keys held during this event.

character property

character: str | None

Typed character, when the key produces text.

from_binding classmethod

from_binding(
    binding: str,
    *,
    kind: KeyboardEventKind = "press",
    character: str | None = None
) -> "KeyboardEventData"

Create keyboard data from a binding string.

Parameters:

  • binding (str) –

    Keyboard binding to synthesize.

  • kind (KeyboardEventKind, default: 'press' ) –

    Keyboard transition.

  • character (str | None, default: None ) –

    Text character, when the binding represents one.

Returns:

  • 'KeyboardEventData'

    Synthetic keyboard event data.

Source code in xnano/events.py
@classmethod
def from_binding(
    cls,
    binding: str,
    *,
    kind: KeyboardEventKind = "press",
    character: str | None = None,
) -> "KeyboardEventData":
    """Create keyboard data from a binding string.

    Args:
        binding: Keyboard binding to synthesize.
        kind: Keyboard transition.
        character: Text character, when the binding represents one.

    Returns:
        Synthetic keyboard event data.
    """
    _, key = parse_binding_tuple(binding)
    if character is None:
        character = (
            key if len(key) == 1 else (" " if key == "space" else None)
        )
    return cls(_kind=kind, _binding=binding, _character=character)

matches

matches(*bindings: KeyboardBinding) -> bool

Return whether this event matches any binding.

Bare modifiers ("shift", "ctrl", "alt") match a modifier-only key press, not a modified key such as "shift+a".

Parameters:

  • *bindings (KeyboardBinding, default: () ) –

    Candidate keyboard bindings.

Returns:

  • bool

    Whether at least one binding matches.

Source code in xnano/events.py
def matches(self, *bindings: KeyboardBinding) -> bool:
    """Return whether this event matches any binding.

    Bare modifiers (``"shift"``, ``"ctrl"``, ``"alt"``) match a
    modifier-only key press, not a modified key such as ``"shift+a"``.

    Args:
        *bindings: Candidate keyboard bindings.

    Returns:
        Whether at least one binding matches.
    """
    native_event: Any = self._native_event
    own = normalize_keyboard_binding(str(self.binding))
    for binding in bindings:
        modifiers, key = normalize_keyboard_binding(str(binding))
        if not modifiers and key in _MODIFIER_KEYS:
            if self._matches_bare_modifier(key):
                return True
            continue
        if native_event is not None:
            try:
                if CoreKeyBinding.parse(str(binding)).matches(
                    native_event
                ):
                    return True
            except Exception:
                continue
        elif (modifiers, key) == own:
            return True
    return False

MouseEventData dataclass

MouseEventData(
    kind: MouseEventKind,
    x: int,
    y: int,
    button: MouseButton = "unknown",
    field: str | None = None,
    group: str | None = None,
)

Bases: AbstractEventData

Mouse input at a cell coordinate.

Attributes:

  • kind (MouseEventKind) –

    Mouse transition.

  • x (int) –

    Zero-based cell column.

  • y (int) –

    Zero-based cell row.

  • button (MouseButton) –

    Button involved in the transition.

  • field (str | None) –

    Field under the pointer for synthetic or web input.

  • group (str | None) –

    Field group under the pointer.

Examples:

event = MouseEventData(kind="press", x=12, y=4, button="left")

type class-attribute

type: Literal['mouse'] = 'mouse'

Payload category.

kind instance-attribute

Mouse transition.

x instance-attribute

x: int

Zero-based cell column.

y instance-attribute

y: int

Zero-based cell row.

button class-attribute instance-attribute

button: MouseButton = 'unknown'

Button involved in the transition.

field class-attribute instance-attribute

field: str | None = None

Field under the pointer.

group class-attribute instance-attribute

group: str | None = None

Field group under the pointer.

ResizeEventData dataclass

ResizeEventData(width: int, height: int)

Bases: AbstractEventData

New viewport dimensions.

Attributes:

  • width (int) –

    Viewport width in cells.

  • height (int) –

    Viewport height in cells.

type class-attribute

type: Literal['resize'] = 'resize'

Payload category.

width instance-attribute

width: int

Viewport width in cells.

height instance-attribute

height: int

Viewport height in cells.

ClipboardEventData dataclass

ClipboardEventData(text: str | None = None)

Bases: AbstractEventData

Text received from paste or clipboard input.

Attributes:

  • text (str | None) –

    Pasted text, when available.

type class-attribute

type: Literal['clipboard'] = 'clipboard'

Payload category.

text class-attribute instance-attribute

text: str | None = None

Pasted text.

FocusEventData dataclass

FocusEventData(
    kind: FocusEventKind,
    field: str | None = None,
    group: str | None = None,
)

Bases: AbstractEventData

Terminal or field focus transition.

Attributes:

type class-attribute

type: Literal['focus'] = 'focus'

Payload category.

kind instance-attribute

Focus transition.

field class-attribute instance-attribute

field: str | None = None

Focused field name.

group class-attribute instance-attribute

group: str | None = None

Focused field group.

TickEventData dataclass

TickEventData(elapsed_ms: int = 0)

Bases: AbstractEventData

Elapsed time reported by the runtime clock.

Attributes:

type class-attribute

type: Literal['tick'] = 'tick'

Payload category.

elapsed_ms class-attribute instance-attribute

elapsed_ms: int = 0

Milliseconds since the previous tick.

Event dataclass

Event(
    _core_event: CoreEvent | None = None,
    _event_data: EventData | None = None,
)

Unified event produced by native input, web input, or an action.

Attributes:

Examples:

event = Event.from_data(KeyboardEventData.from_binding("enter"))
if event.keyboard_event and event.keyboard_event.matches("enter"):
    submit()

Methods:

data property

data: EventData

Public payload, converted lazily from native input.

type property

Category of the event payload.

keyboard_event property

keyboard_event: KeyboardEventData | None

Keyboard payload, or None.

keyboard_key property

keyboard_key: KeyboardKey | str | None

Primary keyboard key, or None.

keyboard_event_kind property

keyboard_event_kind: KeyboardEventKind | None

Keyboard event kind when this is a keyboard event.

keyboard_modifiers property

keyboard_modifiers: list[KeyboardModifier]

Modifier keys when this is a keyboard event.

mouse_event property

mouse_event: MouseEventData | None

Mouse payload, or None.

mouse_position property

mouse_position: tuple[int, int] | None

Mouse cell position, or None.

mouse_event_kind property

mouse_event_kind: MouseEventKind | None

Mouse event kind when this is a mouse event.

mouse_button property

mouse_button: MouseButton | None

Mouse button when this is a mouse event.

resize_event property

resize_event: ResizeEventData | None

Resize payload, or None.

resize_size property

resize_size: tuple[int, int] | None

Viewport size from a resize event, or None.

clipboard_event property

clipboard_event: ClipboardEventData | None

Clipboard payload, or None.

clipboard_text property

clipboard_text: str | None

Pasted text when this is a clipboard event.

focus_event property

focus_event: FocusEventData | None

Focus payload, or None.

tick_event property

tick_event: TickEventData | None

Tick payload, or None.

from_data classmethod

from_data(data: EventData) -> 'Event'

Create an event from a synthetic payload.

Parameters:

Returns:

  • 'Event'

    Event wrapping the payload.

Source code in xnano/events.py
@classmethod
def from_data(cls, data: EventData) -> "Event":
    """Create an event from a synthetic payload.

    Args:
        data: Public event payload.

    Returns:
        Event wrapping the payload.
    """
    return cls(_event_data=data)

is_clipboard_event

is_clipboard_event() -> bool

Return whether this is clipboard input.

Source code in xnano/events.py
def is_clipboard_event(self) -> bool:
    """Return whether this is clipboard input."""
    return self.type == "clipboard"

is_focus_event

is_focus_event() -> bool

Return whether this is a focus transition.

Source code in xnano/events.py
def is_focus_event(self) -> bool:
    """Return whether this is a focus transition."""
    return self.type == "focus"

is_keyboard_event

is_keyboard_event() -> bool

Return whether this is keyboard input.

Source code in xnano/events.py
def is_keyboard_event(self) -> bool:
    """Return whether this is keyboard input."""
    return self.type == "keyboard"

is_mouse_event

is_mouse_event() -> bool

Return whether this is mouse input.

Source code in xnano/events.py
def is_mouse_event(self) -> bool:
    """Return whether this is mouse input."""
    return self.type == "mouse"

is_resize_event

is_resize_event() -> bool

Return whether this is a viewport resize.

Source code in xnano/events.py
def is_resize_event(self) -> bool:
    """Return whether this is a viewport resize."""
    return self.type == "resize"

is_tick_event

is_tick_event() -> bool

Return whether this is a runtime clock tick.

Source code in xnano/events.py
def is_tick_event(self) -> bool:
    """Return whether this is a runtime clock tick."""
    return self.type == "tick"

normalize_keyboard_binding

normalize_keyboard_binding(
    binding: str,
) -> tuple[frozenset[str], str]

Normalize a keyboard binding for comparison.

Parameters:

  • binding (str) –

    Binding such as "ctrl+s".

Returns:

Source code in xnano/events.py
def normalize_keyboard_binding(
    binding: str,
) -> tuple[frozenset[str], str]:
    """Normalize a keyboard binding for comparison.

    Args:
        binding: Binding such as ``"ctrl+s"``.

    Returns:
        Normalized modifiers and primary key.
    """
    parts = [
        part.strip().lower() for part in binding.split("+") if part.strip()
    ]
    if not parts:
        return (frozenset(), "")
    return (
        frozenset(
            part for part in parts[:-1] if part in ("ctrl", "alt", "shift")
        ),
        _KEY_ALIASES.get(parts[-1], parts[-1]),
    )

parse_binding_tuple

parse_binding_tuple(
    binding: str,
) -> tuple[list[KeyboardModifier], str]

Split a binding into modifiers and its primary key.

Parameters:

  • binding (str) –

    Binding such as "shift+tab".

Returns:

Source code in xnano/events.py
def parse_binding_tuple(
    binding: str,
) -> tuple[list[KeyboardModifier], str]:
    """Split a binding into modifiers and its primary key.

    Args:
        binding: Binding such as ``"shift+tab"``.

    Returns:
        Modifier names and primary key.
    """
    modifiers, key = normalize_keyboard_binding(binding)
    return (
        [cast(KeyboardModifier, modifier) for modifier in modifiers],
        "esc" if key == "escape" else key,
    )

event_from_core

event_from_core(core_event: CoreEvent) -> Event

Convert a native core event into the public Event shape.

Browser input and synthetic actions should construct the same public classes; this is the single native conversion path.

Parameters:

  • core_event (CoreEvent) –

    Event polled from CoreSession.

Returns:

  • Event

    A public Event handlers cannot distinguish from other sources.

Source code in xnano/events.py
def event_from_core(core_event: CoreEvent) -> Event:
    """Convert a native core event into the public ``Event`` shape.

    Browser input and synthetic actions should construct the same public
    classes; this is the single native conversion path.

    Args:
        core_event: Event polled from ``CoreSession``.

    Returns:
        A public ``Event`` handlers cannot distinguish from other sources.
    """
    return Event(_core_event=core_event)