mirror of
https://github.com/taigrr/bubbletea.git
synced 2026-04-02 02:59:09 -07:00
Simplify code and add tests for mouse.go (#30)
This commit is contained in:
committed by
GitHub
parent
3aa00243ff
commit
02a0509e34
70
mouse.go
70
mouse.go
@@ -58,6 +58,7 @@ var mouseEventTypes = map[MouseEventType]string{
|
||||
//
|
||||
// ESC [M Cb Cx Cy
|
||||
//
|
||||
// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
||||
func parseX10MouseEvent(buf []byte) (m MouseEvent, err error) {
|
||||
if len(buf) != 6 || string(buf[:3]) != "\x1b[M" {
|
||||
return m, errors.New("not an X10 mouse event")
|
||||
@@ -65,38 +66,55 @@ func parseX10MouseEvent(buf []byte) (m MouseEvent, err error) {
|
||||
|
||||
e := buf[3] - 32
|
||||
|
||||
switch e {
|
||||
case 35:
|
||||
m.Type = MouseMotion
|
||||
case 64:
|
||||
m.Type = MouseWheelUp
|
||||
case 65:
|
||||
m.Type = MouseWheelDown
|
||||
default:
|
||||
switch e & 3 {
|
||||
case 0:
|
||||
if e&64 != 0 {
|
||||
m.Type = MouseWheelUp
|
||||
} else {
|
||||
m.Type = MouseLeft
|
||||
}
|
||||
case 1:
|
||||
if e&64 != 0 {
|
||||
m.Type = MouseWheelDown
|
||||
} else {
|
||||
m.Type = MouseMiddle
|
||||
}
|
||||
case 2:
|
||||
const (
|
||||
bitShift = 0b0000_0100
|
||||
bitAlt = 0b0000_1000
|
||||
bitCtrl = 0b0001_0000
|
||||
bitMotion = 0b0010_0000
|
||||
bitWheel = 0b0100_0000
|
||||
|
||||
bitsMask = 0b0000_0011
|
||||
|
||||
bitsLeft = 0b0000_0000
|
||||
bitsMiddle = 0b0000_0001
|
||||
bitsRight = 0b0000_0010
|
||||
bitsRelease = 0b0000_0011
|
||||
|
||||
bitsWheelUp = 0b0000_0000
|
||||
bitsWheelDown = 0b0000_0001
|
||||
)
|
||||
|
||||
if e&bitWheel != 0 {
|
||||
// Check the low two bits.
|
||||
switch e & bitsMask {
|
||||
case bitsWheelUp:
|
||||
m.Type = MouseWheelUp
|
||||
case bitsWheelDown:
|
||||
m.Type = MouseWheelDown
|
||||
}
|
||||
} else {
|
||||
// Check the low two bits.
|
||||
// We do not separate clicking and dragging.
|
||||
switch e & bitsMask {
|
||||
case bitsLeft:
|
||||
m.Type = MouseLeft
|
||||
case bitsMiddle:
|
||||
m.Type = MouseMiddle
|
||||
case bitsRight:
|
||||
m.Type = MouseRight
|
||||
case 3:
|
||||
m.Type = MouseRelease
|
||||
case bitsRelease:
|
||||
if e&bitMotion != 0 {
|
||||
m.Type = MouseMotion
|
||||
} else {
|
||||
m.Type = MouseRelease
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if e&8 != 0 {
|
||||
if e&bitAlt != 0 {
|
||||
m.Alt = true
|
||||
}
|
||||
if e&16 != 0 {
|
||||
if e&bitCtrl != 0 {
|
||||
m.Ctrl = true
|
||||
}
|
||||
|
||||
|
||||
387
mouse_test.go
Normal file
387
mouse_test.go
Normal file
@@ -0,0 +1,387 @@
|
||||
package tea
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMouseEvent_String(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
event MouseEvent
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "unknown",
|
||||
event: MouseEvent{Type: MouseUnknown},
|
||||
expected: "unknown",
|
||||
},
|
||||
{
|
||||
name: "left",
|
||||
event: MouseEvent{Type: MouseLeft},
|
||||
expected: "left",
|
||||
},
|
||||
{
|
||||
name: "right",
|
||||
event: MouseEvent{Type: MouseRight},
|
||||
expected: "right",
|
||||
},
|
||||
{
|
||||
name: "middle",
|
||||
event: MouseEvent{Type: MouseMiddle},
|
||||
expected: "middle",
|
||||
},
|
||||
{
|
||||
name: "release",
|
||||
event: MouseEvent{Type: MouseRelease},
|
||||
expected: "release",
|
||||
},
|
||||
{
|
||||
name: "wheel up",
|
||||
event: MouseEvent{Type: MouseWheelUp},
|
||||
expected: "wheel up",
|
||||
},
|
||||
{
|
||||
name: "wheel down",
|
||||
event: MouseEvent{Type: MouseWheelDown},
|
||||
expected: "wheel down",
|
||||
},
|
||||
{
|
||||
name: "motion",
|
||||
event: MouseEvent{Type: MouseMotion},
|
||||
expected: "motion",
|
||||
},
|
||||
{
|
||||
name: "alt+left",
|
||||
event: MouseEvent{
|
||||
Type: MouseLeft,
|
||||
Alt: true,
|
||||
},
|
||||
expected: "alt+left",
|
||||
},
|
||||
{
|
||||
name: "ctrl+left",
|
||||
event: MouseEvent{
|
||||
Type: MouseLeft,
|
||||
Ctrl: true,
|
||||
},
|
||||
expected: "ctrl+left",
|
||||
},
|
||||
{
|
||||
name: "ctrl+alt+left",
|
||||
event: MouseEvent{
|
||||
Type: MouseLeft,
|
||||
Alt: true,
|
||||
Ctrl: true,
|
||||
},
|
||||
expected: "ctrl+alt+left",
|
||||
},
|
||||
{
|
||||
name: "ignore coordinates",
|
||||
event: MouseEvent{
|
||||
X: 100,
|
||||
Y: 200,
|
||||
Type: MouseLeft,
|
||||
},
|
||||
expected: "left",
|
||||
},
|
||||
{
|
||||
name: "broken type",
|
||||
event: MouseEvent{
|
||||
Type: MouseEventType(-1000),
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tt {
|
||||
tc := tt[i]
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := tc.event.String()
|
||||
|
||||
if tc.expected != actual {
|
||||
t.Fatalf("expected %q but got %q",
|
||||
tc.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseX10MouseEvent(t *testing.T) {
|
||||
encode := func(b byte, x, y int) []byte {
|
||||
return []byte{
|
||||
'\x1b',
|
||||
'[',
|
||||
'M',
|
||||
byte(32) + b,
|
||||
byte(x + 32 + 1),
|
||||
byte(y + 32 + 1),
|
||||
}
|
||||
}
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
buf []byte
|
||||
expected MouseEvent
|
||||
}{
|
||||
// Position.
|
||||
{
|
||||
name: "zero position",
|
||||
buf: encode(0b0010_0000, 0, 0),
|
||||
expected: MouseEvent{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
Type: MouseLeft,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "max position",
|
||||
buf: encode(0b0010_0000, 222, 222), // Because 255 (max int8) - 32 - 1.
|
||||
expected: MouseEvent{
|
||||
X: 222,
|
||||
Y: 222,
|
||||
Type: MouseLeft,
|
||||
},
|
||||
},
|
||||
// Simple.
|
||||
{
|
||||
name: "left",
|
||||
buf: encode(0b0000_0000, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseLeft,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "left in motion",
|
||||
buf: encode(0b0010_0000, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseLeft,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "middle",
|
||||
buf: encode(0b0000_0001, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseMiddle,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "middle in motion",
|
||||
buf: encode(0b0010_0001, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseMiddle,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "right",
|
||||
buf: encode(0b0000_0010, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseRight,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "right in motion",
|
||||
buf: encode(0b0010_0010, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseRight,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "motion",
|
||||
buf: encode(0b0010_0011, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseMotion,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wheel up",
|
||||
buf: encode(0b0100_0000, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseWheelUp,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wheel down",
|
||||
buf: encode(0b0100_0001, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseWheelDown,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "release",
|
||||
buf: encode(0b0000_0011, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseRelease,
|
||||
},
|
||||
},
|
||||
// Combinations.
|
||||
{
|
||||
name: "alt+right",
|
||||
buf: encode(0b0010_1010, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseRight,
|
||||
Alt: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ctrl+right",
|
||||
buf: encode(0b0011_0010, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseRight,
|
||||
Ctrl: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ctrl+alt+right",
|
||||
buf: encode(0b0011_1010, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseRight,
|
||||
Alt: true,
|
||||
Ctrl: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alt+wheel down",
|
||||
buf: encode(0b0100_1001, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseWheelDown,
|
||||
Alt: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ctrl+wheel down",
|
||||
buf: encode(0b0101_0001, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseWheelDown,
|
||||
Ctrl: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ctrl+alt+wheel down",
|
||||
buf: encode(0b0101_1001, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseWheelDown,
|
||||
Alt: true,
|
||||
Ctrl: true,
|
||||
},
|
||||
},
|
||||
// Unknown.
|
||||
{
|
||||
name: "wheel with unknown bit",
|
||||
buf: encode(0b0100_0010, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseUnknown,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown with modifier",
|
||||
buf: encode(0b0100_1010, 32, 16),
|
||||
expected: MouseEvent{
|
||||
X: 32,
|
||||
Y: 16,
|
||||
Type: MouseUnknown,
|
||||
Alt: true,
|
||||
},
|
||||
},
|
||||
// Overflow position.
|
||||
{
|
||||
name: "overflow position",
|
||||
buf: encode(0b0010_0000, 250, 223), // Because 255 (max int8) - 32 - 1.
|
||||
expected: MouseEvent{
|
||||
X: -6,
|
||||
Y: -33,
|
||||
Type: MouseLeft,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tt {
|
||||
tc := tt[i]
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual, err := parseX10MouseEvent(tc.buf)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
if tc.expected != actual {
|
||||
t.Fatalf("expected %#v but got %#v",
|
||||
tc.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseX10MouseEvent_error(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
buf []byte
|
||||
}{
|
||||
{
|
||||
name: "empty buf",
|
||||
buf: nil,
|
||||
},
|
||||
{
|
||||
name: "wrong high bit",
|
||||
buf: []byte("\x1a[M@A1"),
|
||||
},
|
||||
{
|
||||
name: "short buf",
|
||||
buf: []byte("\x1b[M@A"),
|
||||
},
|
||||
{
|
||||
name: "long buf",
|
||||
buf: []byte("\x1b[M@A11"),
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tt {
|
||||
tc := tt[i]
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := parseX10MouseEvent(tc.buf)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("expected error but got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user