Selkie--Event

NAME

Selkie::Event - Keyboard, mouse, and resize event abstraction

SYNOPSIS

use Selkie::Event;
use Notcurses::Native::Types;

# In a widget's handle-event method:
method handle-event(Selkie::Event $ev --> Bool) {
    return False unless $ev.event-type ~~ KeyEvent;

    given $ev.id {
        when NCKEY_UP    { self!cursor-up;   return True }
        when NCKEY_DOWN  { self!cursor-down; return True }
        when NCKEY_ENTER { self!activate;    return True }
    }

    # Printable character?
    if $ev.char.defined && $ev.char.chars == 1 && $ev.char.ord >= 32 {
        self!insert-char($ev.char);
        return True;
    }

    False;   # not consumed — bubble to parent
}

DESCRIPTION

Every input event — keystrokes, mouse clicks, terminal resizes — is wrapped in a Selkie::Event before reaching widgets. The event carries:

  • An id — the keycode (NCKEY_*) or character codepoint

  • A char — the effective printable character, if any (handles Shift correctly: Shift+1 → '!')

  • The modifiers that were held — a Set of Modifier values

  • The input-type — PRESS, RELEASE, REPEAT (see NcInputType)

  • The event-type — KeyEvent, MouseEvent, or ResizeEvent

  • Mouse coordinates (x, y) for mouse events

Widgets implement handle-event(Selkie::Event) returning Bool. True means the event was consumed; False lets it bubble to the parent chain and eventually to the app's global keybinds.

This module also exports Keybind — the parsed form used by on-key on widgets and Selkie::App.

EXAMPLES

Character input

When the user types a printable character on a focused widget, you get it in $ev.char:

if $ev.char.defined && $ev.char.chars == 1 && $ev.char.ord >= 32 {
    # Printable — insert into buffer
    $!buffer ~= $ev.char;
    $!change-supplier.emit($!buffer);
    return True;
}

Note the .ord = 32> guard: that filters out control characters (which arrive with id in the 1–26 range) so Ctrl+X combos aren't mistaken for typed input.

Checking modifiers

Use has-modifier to test for a specific modifier key:

if $ev.id == NCKEY_ENTER && $ev.has-modifier(Mod-Ctrl) {
    self!submit;        # Ctrl+Enter submits
    return True;
} elsif $ev.id == NCKEY_ENTER {
    self!insert-newline;   # plain Enter inserts a newline
    return True;
}

Mouse events

For mouse events, id is one of the NCKEY_SCROLL_UP, NCKEY_BUTTON1, etc. constants, and x/y give the click coordinates:

if $ev.event-type ~~ MouseEvent {
    given $ev.id {
        when NCKEY_SCROLL_UP   { self!scroll(-1); return True }
        when NCKEY_SCROLL_DOWN { self!scroll(1);  return True }
    }
}

KEYBIND SYNTAX

Keybind.parse and the on-key methods accept a string spec:

  • Single character: 'a', '?', 'Q', '+'

  • Named keys: 'enter', 'tab', 'esc' (or 'escape'), 'space', 'backspace', 'delete', 'insert', 'home', 'end', 'pgup', 'pgdown', 'up', 'down', 'left', 'right'

  • Function keys: 'f1' through 'f60'

  • Modifiers: 'ctrl+', 'alt+', 'shift+', 'super+', 'hyper+', 'meta+' — combinable, e.g. 'ctrl+shift+a'

Letter keybinds are case-insensitive — 'a' matches both a and A (with Shift held).

The literal '+' key is bindable too: write it as '+' on its own, or as 'shift++', 'ctrl++', 'ctrl+shift++', etc. The parser recognises a trailing '+' as the key when the rest of the spec already supplies one or more modifiers.

SEE ALSO

  • Selkie::Widget — widgets receive events via handle-event

  • Selkie::App — the event loop dispatches to focused widget first, then parent chain, then global keybinds

Category of event. KeyEvent for keystrokes, MouseEvent for clicks and scrolls, ResizeEvent for terminal resizes.

Modifier keys. Test with $ev.has-modifier(Mod-Ctrl), etc.

has UInt $.id

The keycode or character codepoint of the event. For named keys this is an NCKEY_* constant; for printable characters it's the ordinal.

has Str $.char

The effective printable character, if any. Respects Shift (Shift+1 → '!'). Undefined for non-printable keys, synthesised events, and legacy control sequences.

has Set $.modifiers

The set of modifier keys held when the event fired. Test with has-modifier.

has NcInputType $.input-type

The input type: NCTYPE_PRESS, NCTYPE_RELEASE, NCTYPE_REPEAT, etc. The framework typically filters RELEASE events before dispatching.

has EventType $.event-type

Which category this event belongs to — see EventType.

has Int $.y

Mouse Y coordinate for MouseEvent, -1 otherwise.

has Int $.x

Mouse X coordinate for MouseEvent, -1 otherwise.

has Int $.click-count

Click multiplicity for MouseEvent presses: 1 for a single click, 2 for a double-click, 3 for a triple-click. 0 for everything else (motion, drag, release, scroll, keyboard / resize events). Computed by Selkie::App from the inter-press timing and target cell — widgets read this to distinguish e.g. "select" from "open" in Selkie::Widget::FileBrowser.

method has-modifier

method has-modifier(
    Modifier $mod
) returns Bool

True if the given modifier is part of the event's modifier set.

method with-click-count

method with-click-count(
    Int $n
) returns Selkie::Event

Return a fresh Selkie::Event identical to this one but with the given click-count. Used by Selkie::App's mouse dispatcher to annotate a press event with its multiplicity before delivery.

method has-any-modifier

method has-any-modifier() returns Bool

True if any modifier is held. Useful for "pass bare keys to the widget, bubble modified keys to global keybinds" branches.

method from-ncinput

method from-ncinput(
    Notcurses::Native::Types::Ncinput $ni
) returns Selkie::Event

Build a Selkie::Event from a raw notcurses Ncinput struct. Called by Selkie::App inside the event loop — you don't normally call this yourself. Handles: resize detection, mouse vs key classification, modifier bit decoding, effective character resolution for Shift + key combos, and legacy Ctrl+A..Z control-code remapping for terminals without the kitty keyboard protocol.

Keybind

A parsed keybind specification, produced by Keybind.parse and matched against events via matches. You don't normally construct or match these yourself — on-key does it for you — but the class is exposed so advanced code can inspect registered binds.

has UInt $.id

The target keycode / character codepoint.

has Str $.char

The target character, if the bind was for a single character.

has Set $.modifiers

The modifier set that must be held for a match.

has Str $.spec

The original spec string the bind was parsed from. Useful for help-overlay rendering ("Ctrl+L — Lorebooks").

has Str $.description

Optional human-readable description of what the bind does. Set via the :description arg on Widget.on-key; surfaced by Selkie::Widget::HelpOverlay.

has Callable &.handler

The handler callable invoked on match.

method parse

method parse(
    Str:D $spec,
    &handler,
    Str :$description = ""
) returns Selkie::Event::Keybind

Parse a keybind spec string into a Keybind. Spec grammar is described under KEYBIND SYNTAX in this module's main pod. Throws on unknown modifiers or unknown key names.

method matches

method matches(
    Selkie::Event $ev
) returns Bool

Does the given event match this keybind? Letter binds are case-insensitive — 'a' matches a typed A with Shift held. All other binds require an exact modifier-set match.

MouseHandler

A registered click / scroll / drag / mouse-down / mouse-up handler, produced by the on-click / on-scroll / etc. methods on Selkie::Widget. Surfaced via mouse-handlers so help overlays can list registered mouse interactions alongside keyboard binds. Apps don't normally construct these directly.

has Str $.kind

The kind of event this handler responds to: one of 'click', 'scroll', 'drag', 'mouse-down', 'mouse-up'.

has UInt $.button

The button this handler fires on (1=primary, 2=middle, 3=right). 0 is the wildcard "any button" — used by on-scroll (where the wheel arrives as buttons 4 and 5) and by low-level handlers that want to see every press.

has Callable &.handler

The handler callable; receives the Selkie::Event.

has Str $.description

Optional human-readable description, surfaced in help overlays.

sub mouse-event-kinds

sub mouse-event-kinds(
    Selkie::Event $ev
) returns List

Classify a mouse event into the list of MouseHandler.kinds it should fan out to. A press fires both 'click' and 'mouse-down' handlers; a release fires 'mouse-up'; drag motion fires 'drag'; scroll wheel fires 'scroll'. Empty list for events that don't match any kind (e.g. unknown id).

sub mouse-event-button

sub mouse-event-button(
    Selkie::Event $ev
) returns UInt

Extract the 1-indexed button number from a mouse event id. Scroll events are buttons 4 and 5 by encoding; pure motion (NCKEY_MOTION) has no associated button and returns 0.

Selkie v0.10.0

High-level TUI framework built on Notcurses

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

Notcurses::Native:ver<0.4.0+>:auth<zef:apogee>

Test Dependencies

Provides

  • Selkie
  • Selkie::App
  • Selkie::App::Internal::Dispatch
  • Selkie::App::Internal::ErrorLog
  • Selkie::App::Internal::FocusTree
  • Selkie::App::Internal::HitTest
  • Selkie::App::Internal::IdleBudget
  • Selkie::App::Internal::OverlayTree
  • Selkie::App::Internal::RenderLoop
  • Selkie::App::Internal::ScreenModalLifecycle
  • Selkie::App::Internal::Terminal
  • Selkie::App::Internal::TerminalSequences
  • Selkie::Container
  • Selkie::EffectiveBounds
  • Selkie::Event
  • Selkie::Layout::Allocate
  • Selkie::Layout::HBox
  • Selkie::Layout::Split
  • Selkie::Layout::VBox
  • Selkie::Plot::Palette
  • Selkie::Plot::Scaler
  • Selkie::Plot::Ticks
  • Selkie::ScreenManager
  • Selkie::Sizing
  • Selkie::Store
  • Selkie::Style
  • Selkie::Test::Focus
  • Selkie::Test::Keys
  • Selkie::Test::Snapshot
  • Selkie::Test::Snapshot::Harness
  • Selkie::Test::Store
  • Selkie::Test::Supply
  • Selkie::Test::Tree
  • Selkie::Theme
  • Selkie::Trace
  • Selkie::Tree
  • Selkie::Widget
  • Selkie::Widget::Axis
  • Selkie::Widget::BarChart
  • Selkie::Widget::Border
  • Selkie::Widget::Button
  • Selkie::Widget::CardList
  • Selkie::Widget::Checkbox
  • Selkie::Widget::CommandPalette
  • Selkie::Widget::ConfirmModal
  • Selkie::Widget::FileBrowser
  • Selkie::Widget::FocusableByDefault
  • Selkie::Widget::Heatmap
  • Selkie::Widget::HelpOverlay
  • Selkie::Widget::Histogram
  • Selkie::Widget::Image
  • Selkie::Widget::Legend
  • Selkie::Widget::LineChart
  • Selkie::Widget::ListView
  • Selkie::Widget::Modal
  • Selkie::Widget::MultiLineInput
  • Selkie::Widget::PasswordStrength
  • Selkie::Widget::Plot
  • Selkie::Widget::ProgressBar
  • Selkie::Widget::RadioGroup
  • Selkie::Widget::RichText
  • Selkie::Widget::RichText::Span
  • Selkie::Widget::ScatterPlot
  • Selkie::Widget::ScrollView
  • Selkie::Widget::Select
  • Selkie::Widget::Sparkline
  • Selkie::Widget::Spinner
  • Selkie::Widget::TabBar
  • Selkie::Widget::Table
  • Selkie::Widget::Text
  • Selkie::Widget::TextInput
  • Selkie::Widget::TextInput::HighlightSpan
  • Selkie::Widget::TextStream
  • Selkie::Widget::Toast
  • Selkie::Widget::ViewportedCardList

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.