mouse

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 2 Imported by: 0

Documentation

Overview

Package mouse carries the mouse event tuilib components actually handle, and the click-counting that turns raw presses into single and double clicks.

Components never see bubbletea's tea.MouseMsg. The app shell translates it into a mouse.Msg first, adding the one thing a component can't work out on its own: whether this press is the second of a double click. Detection needs a timestamp, a previous position, and a configurable threshold, and keeping all three in the shell means no component holds mouse state and no component needs a threshold plumbed into its Options.

A component handles mouse input by matching mouse.Msg and testing the rect it was last given:

case mouse.Msg:
    if !m.rect.Hit(msg.X, msg.Y) {
        return m, nil          // not ours — let a sibling claim it
    }
    if msg.IsDoubleClick() {
        return m, m.activate()
    }
    if msg.IsPress() {
        return m, focus.Request(m)
    }

Rect.Hit already rejects events aimed at a component that wasn't drawn in the current frame, so a hidden pane declines everything without needing to know it's hidden.

Index

Constants

View Source
const DefaultDoubleClickInterval = 500 * time.Millisecond

DefaultDoubleClickInterval is the window in which a second press in the same cell counts as a double click. 500ms is the common desktop default and forgiving enough for terminals over a slow link.

Variables

This section is empty.

Functions

This section is empty.

Types

type Msg

type Msg struct {
	tea.MouseMsg

	// Clicks is 1 for a single press, 2 for the second press of a double
	// click, and 0 for anything that isn't a button press (wheel, motion,
	// release). It does not climb past 2 — a triple click reads as a fresh
	// single click, since nothing in tuilib binds one.
	Clicks int
}

Msg is a mouse event with its click count resolved. It embeds bubbletea's event, so X, Y, Action, Button and the modifier flags read through directly.

func (Msg) IsDoubleClick

func (m Msg) IsDoubleClick() bool

IsDoubleClick reports whether this press completes a double click. Rule 14 maps it to the same verb as enter — open whatever is under the cursor.

func (Msg) IsPointPress added in v0.21.0

func (m Msg) IsPointPress() bool

IsPointPress reports whether this press is pointing at something — either button.

Components use it where the verb is "focus this and put the cursor here", which both buttons mean: a right-click opens a menu about the row under the pointer, so that row has to become the selection first. Only IsPress may *activate* something, which is what keeps a right-click from committing a modal button or flipping a toggle.

func (Msg) IsPress

func (m Msg) IsPress() bool

IsPress reports whether this is a left-button press. This is the event components act on: acting on press rather than release keeps clicks feeling immediate, and matches how lazygit and htop behave.

func (Msg) IsRightPress added in v0.21.0

func (m Msg) IsRightPress() bool

IsRightPress reports whether this is a right-button press — the gesture that asks "what can I do to this?" and opens the action menu.

It carries no click count. Nothing in tuilib binds a right double click, and the Tracker only counts left presses, so a second right press is simply another right press.

Worth knowing before you rely on it: whether this ever arrives is the terminal's decision. Most emulators forward right presses once mouse reporting is on, but some (macOS Terminal.app) always show their own context menu instead. Right-click is therefore an accelerator on top of a keyboard path, never the only way to reach something.

func (Msg) IsWheelDown

func (m Msg) IsWheelDown() bool

func (Msg) IsWheelUp

func (m Msg) IsWheelUp() bool

IsWheelUp / IsWheelDown report vertical wheel movement. Rule 23 makes the wheel behave exactly as the up and down arrows do for the component under the pointer.

type Tracker

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

Tracker converts tea.MouseMsg into Msg, counting rapid repeat presses in the same cell as double clicks. The app shell owns one; callers running without the shell can use one directly.

The zero Tracker uses DefaultDoubleClickInterval.

func NewTracker

func NewTracker(interval time.Duration) Tracker

NewTracker returns a Tracker using the given double-click window. A non-positive interval falls back to DefaultDoubleClickInterval.

func (*Tracker) SetInterval

func (t *Tracker) SetInterval(d time.Duration)

SetInterval updates the double-click window.

func (*Tracker) Track

func (t *Tracker) Track(e tea.MouseMsg, now time.Time) Msg

Track resolves e into a Msg. now is passed in rather than read from the clock so the behaviour is testable.

A press counts as a double click when it lands in the same cell as the previous press and within the interval. Requiring the same cell — rather than merely a nearby one — means a fast click on one list row followed by a fast click on another reads as two separate selections, which is what the user meant.

Jump to

Keyboard shortcuts

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