pointer

package
v1.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package pointer maps rendered terminal cells to pointer interactions.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Cancel

func Cancel() tea.Cmd

Cancel clears any active press when the original control disappeared between render passes, for example after a resize or asynchronous refresh.

func CancelWith

func CancelWith(followup tea.Cmd) tea.Cmd

CancelWith clears capture before forwarding a resolved domain command. The command is executed only when the mailbox drains it inside ordered Update.

func IsMessage

func IsMessage(message tea.Msg) bool

IsMessage reports whether message carries pointer feedback that State.Update must consume before an active overlay routes domain messages.

func ReplaceActivation

func ReplaceActivation(message tea.Msg, action Action) tea.Msg

ReplaceActivation binds a resolved release to the current stable action for the same control identity. It does not perform a coordinate lookup.

func ReplaceFollowup

func ReplaceFollowup(message tea.Msg, followup tea.Msg) tea.Msg

func ResetHover

func ResetHover() tea.Cmd

ResetHover clears both the active owner's hover feedback and its retained observation. Capture is deliberately left to Cancel.

Types

type Action

type Action func(Point) tea.Msg

Action builds the model message produced by one pointer activation.

type Choice

type Choice struct {
	Cursor int  // the keyboard cursor
	Rows   Rows // this surface's own id-to-row resolver
}

Choice is the mouse-mode machine of spec section 10.5.2, for one overlay choice surface. Ratified call 9: the machine runs on the checklist, the drift list, the ADR-split stories, the issue-import list, the pickers and the settings rows, and on nothing else. The board cursor never follows the pointer, because it is the drag source, the anchor every board keybinding resolves against and the card the detail overlay opens.

The machine is pure. It holds no clock, no flag and no copy of hover: mouse mode is on exactly when the caller's State has a hovered id that Rows resolves, so there is one bit of state and it lives in State.

func (Choice) Acting

func (c Choice) Acting(state State) int

Acting returns the row that renders this surface's cursor cue: the hovered row while mouse mode is on, the keyboard cursor otherwise. Exactly one cursor is visible at any moment, which is the failure the machine exists to prevent.

func (Choice) Adopt

func (c Choice) Adopt(state State, arrow bool) (int, State)

Adopt applies rows 7 and 8 to one key press and returns the anchor the key's own motion runs from, plus the state with mouse mode turned off.

The ordering of row 7 is normative and is the row most easily got wrong: adopt, then move. A down arrow while row 7 is hovered lands on row 8, not on cursor+1, so the caller applies its own motion to the returned anchor. Row 8 is the opposite and equally deliberate: a hotkey, Enter or Esc acts on the keyboard cursor, never on whatever the pointer happens to be resting over, because a key typed without looking at the mouse must not be redirected by it.

A press while mouse mode is off for this surface leaves both results alone.

func (Choice) Mode

func (c Choice) Mode(state State) bool

Mode reports whether mouse mode is on for this surface.

type Clicks

type Clicks struct {
	// contains filtered or unexported fields
}

Clicks is the double-click classifier of spec section 10.3.5. It lives here rather than in a view because classification is a pointer concern: a view that had to remember the previous click would grow a second, parallel gesture machine beside this one.

A click is a double-click when it lands within Timing.DoubleClickWindow of the previous click, on the same region id, and the previous click's gesture ended with dragged == false. A click on a different id resets the window; so does any drag. The drag exclusion is not optional - kb's board is a drag-and-drop miller board and a lift that ends on its origin must never register as a double-click.

The window closes because a message arrived, not because a render compared time.Since to a token: Click hands back the command that arms it, and Expire consumes it. Under collapsed timing the arming command dispatches immediately, so a collapsed program classifies every click as a single - which is the deterministic reading a golden needs.

func (Clicks) Armed

func (c Clicks) Armed() (ControlID, bool)

Armed reports whether a click window is open, and on which region.

func (Clicks) Click

func (c Clicks) Click(id ControlID, dragged bool, window time.Duration) (Clicks, bool, tea.Cmd)

Click records a completed click on id and reports whether it closes a double-click. dragged excludes a lift that ended on its origin.

The returned command arms the window for the next click. It is nil when this click was itself the second half of a double-click, and when the gesture dragged: neither leaves a window open.

func (Clicks) Expire

func (c Clicks) Expire(message tea.Msg) (Clicks, bool)

Expire consumes the window-expiry message. The second return reports whether the message belonged to this machine, so a caller can stop routing it.

func (Clicks) Reset

func (c Clicks) Reset() Clicks

Reset closes any open window. A drag, a resize or an overlay taking the input focus all end the gesture the window belonged to.

type ControlID

type ControlID string

ControlID is a stable identifier for one rendered control.

type Interaction

type Interaction struct {
	Kind     InteractionKind
	ID       ControlID
	Point    Point
	Followup tea.Msg
}

Interaction is the immutable semantic identity resolved by a rendered map. Activation remains private to State.Update.

func ObserveInteraction

func ObserveInteraction(message tea.Msg) (Interaction, bool)

type InteractionKind

type InteractionKind uint8
const (
	InteractionPress InteractionKind = iota + 1
	InteractionRelease
	InteractionCancel
	InteractionHover
	InteractionResetHover
)

type Map

type Map struct {
	// contains filtered or unexported fields
}

Map resolves rendered terminal regions to model messages.

func (*Map) Add

func (m *Map) Add(rect Rect, action Action)

Add registers an action region. Later regions take precedence when they overlap.

func (*Map) AddBackdrop

func (m *Map) AddBackdrop(bounds, pane Rect, action Action)

AddBackdrop registers the portion of bounds outside pane as one action.

func (*Map) AddBackdropControl

func (m *Map) AddBackdropControl(id ControlID, bounds, pane Rect, action Action)

AddBackdropControl registers a stable tracked dismissal region outside pane. One identity spans the four non-overlapping strips, so capture survives a handler replacement between press and release.

func (*Map) AddControl

func (m *Map) AddControl(id ControlID, rect Rect, action Action)

AddControl registers an action region with opt-in pressed feedback. IDs must remain stable across render passes. An empty ID retains Add's legacy behavior.

func (*Map) AddWheel

func (m *Map) AddWheel(rect Rect, action func(delta int) tea.Msg)

AddWheel registers a wheel zone. The action receives -1 for up and +1 for down.

func (Map) Handler

func (m Map) Handler() func(tea.MouseMsg) tea.Cmd

Handler returns the immutable render snapshot's mouse callback.

func (Map) Resolve

func (m Map) Resolve(point Point) (ControlID, bool)

Resolve returns the hoverable control containing point, topmost first. It is the region scan of Handler's motion path, exposed for the re-resolve rows 6 and 9 of spec section 10.5.2 drive after the content moved under a still pointer.

func (Map) Snapshot

func (m Map) Snapshot() Snapshot

func (Map) Topology

func (m Map) Topology() Topology

Topology returns the stable controls and wheel bounds owned by this map. Later registrations win just as they do in the hit map.

type Point

type Point struct {
	X int
	Y int
}

Point is a zero-based terminal cell.

type Rect

type Rect struct {
	X0 int
	Y0 int
	X1 int
	Y1 int
}

Rect is a half-open rectangle in zero-based terminal cells.

type Rows

type Rows func(ControlID) (int, bool)

Rows maps a hovered control id onto a row index of one surface. A control that belongs to a different surface, or to no row at all, reports false: that is how a surface decides whether mouse mode is on for it rather than for the panel next door.

func RowsWithPrefix

func RowsWithPrefix(prefix string) Rows

RowsWithPrefix is the resolver for the surfaces that key a row's control id as a fixed prefix plus the row index, which is every choice surface in the TUI. An id that does not carry the prefix, or whose tail is not a non-negative decimal, belongs to another surface.

type Snapshot

type Snapshot struct {
	// contains filtered or unexported fields
}

Snapshot is an immutable copy of one rendered pointer map.

func (Snapshot) Handler

func (s Snapshot) Handler() func(tea.MouseMsg) tea.Cmd

func (Snapshot) Resolve

func (s Snapshot) Resolve(point Point) (ControlID, bool)

func (Snapshot) Topology

func (s Snapshot) Topology() Topology

Topology returns the stable controls and wheel bounds owned by this snapshot.

type State

type State struct {
	// contains filtered or unexported fields
}

State tracks transient pointer feedback independently from domain state.

Hover mirrors press: one control id and the cell it resolved from. Spec section 10.5.2: mouse mode is not a stored flag, it is the hovered id being set and resolving to one of a surface's own regions, so there is exactly one bit of state to clear and no second copy of the same fact to disagree with.

func (State) Active

func (s State) Active() bool

Active reports whether any rendered control owns the current press.

func (State) ClearCapture

func (s State) ClearCapture() State

ClearCapture cancels pressed feedback without changing the last hover observation. Root pointer admission uses it when raw correlation fails.

func (State) ClearHover

func (s State) ClearHover() State

ClearHover turns mouse mode off for every surface. Spec section 10.5.2: turning mouse mode off is clearing hover, and that is the whole of it.

func (State) ClearHoverObservation

func (s State) ClearHoverObservation() State

ClearHoverObservation removes both hover feedback and the retained terminal cell. Admission uses it when raw input cannot be correlated with the frame that resolved it; keeping that cell would let a later render resurrect a hover the user never actually delivered.

func (State) Hover

func (s State) Hover(id ControlID, at Point) State

Hover sets the hovered control and the cell it resolved from. An empty id clears hover while retaining the point, which is what a motion onto an overlay's own backdrop reports.

func (State) HoverPoint

func (s State) HoverPoint() (Point, bool)

HoverPoint returns the last observed pointer cell. The second result is false only before any hover observation; an observation that resolved to no control still retains its cell so stationary re-resolution can discover moved content.

func (State) Hovered

func (s State) Hovered() ControlID

Hovered returns the control under the pointer, empty when there is none.

func (State) IsHovered

func (s State) IsHovered(id ControlID) bool

IsHovered reports whether the identified control is under the pointer. Callers use this while rendering the control's hovered style.

func (State) IsPressed

func (s State) IsPressed(id ControlID) bool

IsPressed reports whether the identified control owns the active pointer press. Callers use this while rendering the control's pressed style.

func (State) Pressed

func (s State) Pressed() ControlID

Pressed returns the stable identity that owns capture.

func (State) Render

func (s State) Render(styles *theme.Styles, id ControlID, content string) string

Render applies same-width pressed feedback to the active control. The caller supplies already-sanitized terminal content.

Spec section 9.1: the feedback is theme.Styles.Pressed, not a raw escape written here. The theme owns the attribute and the re-arming a composed run needs; this package only decides which control wears it.

func (State) Reresolve

func (s State) Reresolve(m Map) State

Reresolve re-derives hover from the retained point against a freshly built map. Rows 6 and 9 of spec section 10.5.2: the pointer can stand still while the content moves under it, so a scroll, a resize or a changed filter has to re-resolve rather than wait for a motion that never comes. A point that no longer lands on a hoverable region clears hover.

func (State) Update

func (s State) Update(message tea.Msg) (State, tea.Cmd, bool)

Update consumes pointer feedback messages. The returned command emits the control's domain message after release feedback has been cleared.

type Surface

type Surface struct {
	Content  string
	Pointer  func(tea.MouseMsg) tea.Cmd
	Topology Topology
}

Surface couples rendered content with the pointer map from that render pass.

type Topology

type Topology struct {
	// contains filtered or unexported fields
}

Topology is the immutable semantic half of one published pointer map. It deliberately carries no rectangles: stale input may rebind a stable control or wheel identity to the current action and bounds, but it may never repeat a coordinate lookup against a frame the terminal did not display.

func (Topology) HasControl

func (t Topology) HasControl(id ControlID) bool

HasControl reports whether the current published owner still exposes id.

func (Topology) Merge

func (t Topology) Merge(other Topology) Topology

Merge returns an immutable union in which bindings from other take precedence. It is used by composite surfaces such as the board, whose hover and activation maps share one published owner.

func (Topology) RebindExact

func (t Topology) RebindExact(message tea.Msg) (tea.Msg, bool)

RebindExact validates a stale exact interaction by stable ID and, for a release, replaces the old frame's activation closure with the current one. Coordinates are retained only as action arguments; they are never resolved.

func (Topology) RebindWheel

func (t Topology) RebindWheel(key string, target int) (tea.Msg, WheelIntent, bool)

RebindWheel rebuilds target against the current binding for key. The caller owns accumulation; topology owns the current absolute bounds and message.

func (Topology) SameControls

func (t Topology) SameControls(other Topology) bool

SameControls reports semantic topology parity without comparing closures.

func (Topology) WithWheel

func (t Topology) WithWheel(intent WheelIntent, rebuild func(int) tea.Msg) Topology

WithWheel returns a topology extended with one current absolute wheel binding. Custom surfaces whose wheel resolver does not use Map.AddWheel use this without exposing their geometry.

type Viewport

type Viewport struct {
	Rect   Rect
	Scroll int
}

Viewport projects logical content rows into a visible terminal rectangle.

func (Viewport) Row

func (v Viewport) Row(logicalRow, x0, x1 int) (Rect, bool)

Row projects one logical content-row span into the viewport and clips it.

type WheelIntent

type WheelIntent struct {
	Key     string
	Current int
	Target  int
	Min     int
	Max     int
}

type WheelMessage

type WheelMessage interface {
	PointerWheelIntent() WheelIntent
	PointerWheelTarget(int) tea.Msg
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL