okami.mom/ultraviolet

git clone https://git.nkp.pet/okami.mom/ultraviolet

Ayman BagabasMerge pull request #112 from charmbracelet/mouse-pixel6603726

main
4.8 KiB150 linesraw
1package uv
2
3import (
4	"github.com/charmbracelet/x/ansi"
5)
6
7// MouseMode represents the mouse tracking mode for the terminal.
8type MouseMode uint8
9
10// Mouse tracking modes.
11//
12// These determine which mouse events the terminal reports.
13const (
14	MouseModeNone   MouseMode = iota // Disable mouse tracking.
15	MouseModePress                   // Press only (DEC mode 9). Reports button press events.
16	MouseModeClick                   // Click tracking (DEC mode 1000). Reports button press and release.
17	MouseModeDrag                    // Drag tracking (DEC mode 1002). Reports press, release, and drag.
18	MouseModeMotion                  // Motion tracking (DEC mode 1003). Reports all mouse events including motion.
19)
20
21// MouseEncoding represents the encoding used for mouse events.
22type MouseEncoding uint8
23
24// Mouse encodings.
25//
26// These determine how mouse coordinates and buttons are encoded in the
27// terminal's escape sequences. The encoding is only meaningful when mouse
28// tracking is enabled via [MouseMode].
29const (
30	MouseEncodingLegacy   MouseEncoding = iota // Legacy X10-compatible encoding. Coordinates limited to 223.
31	MouseEncodingSGR                           // SGR encoding (DEC mode 1006). No coordinate limit, distinguishes press/release.
32	MouseEncodingSGRPixel                      // SGR-pixel encoding (DEC mode 1016). Reports pixel coordinates.
33
34	// TODO: support these additional encodings in the future.
35	// MouseEncodingUTF8                          // UTF-8 encoding (DEC mode 1005). Coordinates limited to 223.
36	// MouseEncodingUrxvt                         // urxvt encoding (DEC mode 1015). No coordinate limit.
37	// MouseEncodingSGRPixel                      // SGR-pixel encoding (DEC mode 1016). Reports pixel coordinates.
38)
39
40// MouseButton represents the button that was pressed during a mouse message.
41type MouseButton = ansi.MouseButton
42
43// Mouse event buttons
44//
45// This is based on X11 mouse button codes.
46//
47//	1 = left button
48//	2 = middle button (pressing the scroll wheel)
49//	3 = right button
50//	4 = turn scroll wheel up
51//	5 = turn scroll wheel down
52//	6 = push scroll wheel left
53//	7 = push scroll wheel right
54//	8 = 4th button (aka browser backward button)
55//	9 = 5th button (aka browser forward button)
56//	10
57//	11
58//
59// Other buttons are not supported.
60const (
61	MouseNone       = ansi.MouseNone
62	MouseLeft       = ansi.MouseLeft
63	MouseMiddle     = ansi.MouseMiddle
64	MouseRight      = ansi.MouseRight
65	MouseWheelUp    = ansi.MouseWheelUp
66	MouseWheelDown  = ansi.MouseWheelDown
67	MouseWheelLeft  = ansi.MouseWheelLeft
68	MouseWheelRight = ansi.MouseWheelRight
69	MouseBackward   = ansi.MouseBackward
70	MouseForward    = ansi.MouseForward
71	MouseButton10   = ansi.MouseButton10
72	MouseButton11   = ansi.MouseButton11
73)
74
75// Mouse represents a Mouse message. Use [MouseEvent] to represent all mouse
76// messages.
77//
78// The X and Y coordinates are zero-based, with (0,0) being the upper left
79// corner of the terminal. When using [MouseEncodingSGRPixel] (DEC mode 1016),
80// X and Y are in pixel coordinates; otherwise they are in cell coordinates.
81//
82//	// Catch all mouse events
83//	switch Event := Event.(type) {
84//	case MouseEvent:
85//	    m := Event.Mouse()
86//	    fmt.Println("Mouse event:", m.X, m.Y, m)
87//	}
88//
89//	// Only catch mouse click events
90//	switch Event := Event.(type) {
91//	case MouseClickEvent:
92//	    fmt.Println("Mouse click event:", Event.X, Event.Y, Event)
93//	}
94type Mouse struct {
95	X, Y   int
96	Button MouseButton
97	Mod    KeyMod
98}
99
100// String returns a string representation of the mouse message.
101func (m Mouse) String() (s string) {
102	if m.Mod.Contains(ModCtrl) {
103		s += "ctrl+"
104	}
105	if m.Mod.Contains(ModAlt) {
106		s += "alt+"
107	}
108	if m.Mod.Contains(ModShift) {
109		s += "shift+"
110	}
111
112	str := m.Button.String()
113	if str == "" {
114		s += "unknown"
115	} else if str != "none" { // motion events don't have a button
116		s += str
117	}
118
119	return s
120}
121
122// MousePixelToCell converts a mouse event with pixel coordinates to cell
123// coordinates.
124//
125// This is only meaningful when using [MouseEncodingSGRPixel] encoding, which
126// reports mouse coordinates in pixels rather than cell units. The conversion
127// is based on the terminal's reported pixel dimensions and cell dimensions.
128//
129// On Windows, or other platforms where [Terminal.GetWinsize] doesn't report
130// pixel dimensions, you can query for the terminal window pixel dimensions via
131// [ansi.WindowOp](4) and get back a [PixelSizeEvent] with the pixel
132// dimensions. You can then construct a [Winsize] struct with the pixel
133// dimensions and the cell dimensions from [Terminal.GetWinsize] and pass it to
134// this function.
135func MousePixelToCell(m Mouse, ws *Winsize) Mouse {
136	var col, row int
137	if ws.Xpixel > 0 {
138		col = m.X * int(ws.Col) / int(ws.Xpixel)
139	}
140	if ws.Ypixel > 0 {
141		row = m.Y * int(ws.Row) / int(ws.Ypixel)
142	}
143
144	return Mouse{
145		X:      col,
146		Y:      row,
147		Button: m.Button,
148		Mod:    m.Mod,
149	}
150}