[GH-ISSUE #752] Application.SetMouseCapture(...) with event nil on Mouse[...]Click #551

Closed
opened 2026-03-04 01:05:56 +03:00 by kerem · 2 comments
Owner

Originally created by @RasmusLindroth on GitHub (Aug 3, 2022).
Original GitHub issue: https://github.com/rivo/tview/issues/752

Hi,

If you set your own global mouse input handler with Application.SetMouseCapture(...) that always returns return nil, action mouse clicks will get an event that is nil. Other mouse actions still gets an event. Because the event is nil I can't check what primitive that were clicked.

Here comes an example program that shows this issue. Example output is presented below.

package main

import (
	"fmt"
	"os"

	"github.com/gdamore/tcell/v2"
	"github.com/rivo/tview"
)

var MouseNamed = []string{
	"MouseMove", "MouseLeftDown", "MouseLeftUp", "MouseLeftClick",
	"MouseLeftDoubleClick", "MouseMiddleDown", "MouseMiddleUp",
	"MouseMiddleClick", "MouseMiddleDoubleClick", "MouseRightDown",
	"MouseRightUp", "MouseRightClick", "MouseRightDoubleClick",
	"MouseScrollUp", "MouseScrollDown", "MouseScrollLeft",
	"MouseScrollRight",
}

type Tv struct {
	TxtView *tview.TextView
}

func main() {
	tv := Tv{
		TxtView: tview.NewTextView(),
	}
	btn := tview.NewButton("Test")
	btn.SetSelectedFunc(func() {
		panic("Button click shouldn't be possible")
	})
	flex := tview.NewFlex().SetDirection(tview.FlexRow).
		AddItem(tv.TxtView, 0, 2, false).
		AddItem(btn, 0, 1, false)
	app := tview.NewApplication().
		EnableMouse(true).
		SetMouseCapture(tv.MouseInput)
	if err := app.SetRoot(flex, true).Run(); err != nil {
		os.Exit(0)
	}
}

func (tv *Tv) MouseInput(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
	if event != nil {
		x, y := event.Position()
		tv.TxtView.Write([]byte(fmt.Sprintf("%s %d %d\n", MouseNamed[action], x, y)))
	} else {
		tv.TxtView.Write([]byte(fmt.Sprintf("%s and event is nil\n", MouseNamed[action])))
	}
	return nil, action
}

Example output:

// Actual output
MouseMove 19 2
MouseMove 20 2
MouseLeftDown 20 2
MouseLeftUp 20 2
MouseLeftClick and event is nil

// Expected output
MouseMove 19 2
MouseMove 20 2
MouseLeftDown 20 2
MouseLeftUp 20 2
MouseLeftClick 20 2

A fix for this in application.go#407, but I don't know if this is the best fix or if it's causing some other issue.

// Intercept event.
if a.mouseCapture != nil {
	tmpEvent := event
	event, action = a.mouseCapture(event, action)
	if event == nil {
		consumed = true
		event = tmpEvent
		return // Don't forward event.
	}
}
Originally created by @RasmusLindroth on GitHub (Aug 3, 2022). Original GitHub issue: https://github.com/rivo/tview/issues/752 Hi, If you set your own global mouse input handler with `Application.SetMouseCapture(...)` that always returns `return nil, action` mouse clicks will get an event that is nil. Other mouse actions still gets an event. Because the event is nil I can't check what primitive that were clicked. Here comes an example program that shows this issue. Example output is presented below. ```go package main import ( "fmt" "os" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) var MouseNamed = []string{ "MouseMove", "MouseLeftDown", "MouseLeftUp", "MouseLeftClick", "MouseLeftDoubleClick", "MouseMiddleDown", "MouseMiddleUp", "MouseMiddleClick", "MouseMiddleDoubleClick", "MouseRightDown", "MouseRightUp", "MouseRightClick", "MouseRightDoubleClick", "MouseScrollUp", "MouseScrollDown", "MouseScrollLeft", "MouseScrollRight", } type Tv struct { TxtView *tview.TextView } func main() { tv := Tv{ TxtView: tview.NewTextView(), } btn := tview.NewButton("Test") btn.SetSelectedFunc(func() { panic("Button click shouldn't be possible") }) flex := tview.NewFlex().SetDirection(tview.FlexRow). AddItem(tv.TxtView, 0, 2, false). AddItem(btn, 0, 1, false) app := tview.NewApplication(). EnableMouse(true). SetMouseCapture(tv.MouseInput) if err := app.SetRoot(flex, true).Run(); err != nil { os.Exit(0) } } func (tv *Tv) MouseInput(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { if event != nil { x, y := event.Position() tv.TxtView.Write([]byte(fmt.Sprintf("%s %d %d\n", MouseNamed[action], x, y))) } else { tv.TxtView.Write([]byte(fmt.Sprintf("%s and event is nil\n", MouseNamed[action]))) } return nil, action } ``` Example output: ``` // Actual output MouseMove 19 2 MouseMove 20 2 MouseLeftDown 20 2 MouseLeftUp 20 2 MouseLeftClick and event is nil // Expected output MouseMove 19 2 MouseMove 20 2 MouseLeftDown 20 2 MouseLeftUp 20 2 MouseLeftClick 20 2 ``` A fix for this in [application.go#407](https://github.com/rivo/tview/blob/master/application.go#L407), but I don't know if this is the best fix or if it's causing some other issue. ```Go // Intercept event. if a.mouseCapture != nil { tmpEvent := event event, action = a.mouseCapture(event, action) if event == nil { consumed = true event = tmpEvent return // Don't forward event. } } ```
kerem closed this issue 2026-03-04 01:05:57 +03:00
Author
Owner

@rivo commented on GitHub (Aug 11, 2022):

Thanks for letting me know about this. The issue here is that a "mouse up" event is followed by a "mouse click" event in one go because "mouse click" is a "secondary event" triggered by a sequence of "primary events" like "mouse down", "move move", and "mouse up". If you prevent the "mouse up" event by returning nil, the "mouse click" event should not be fired. So the expected output is actually:

// Expected output
MouseMove 19 2
MouseMove 20 2
MouseLeftDown 20 2
MouseLeftUp 20 2
// No MouseLeftClick event

The latest commit should result in this.

If you want to process the click event, you need to let the "mouse up" event come through.

<!-- gh-comment-id:1212472295 --> @rivo commented on GitHub (Aug 11, 2022): Thanks for letting me know about this. The issue here is that a "mouse up" event is followed by a "mouse click" event in one go because "mouse click" is a "secondary event" triggered by a sequence of "primary events" like "mouse down", "move move", and "mouse up". If you prevent the "mouse up" event by returning `nil`, the "mouse click" event should not be fired. So the expected output is actually: ``` // Expected output MouseMove 19 2 MouseMove 20 2 MouseLeftDown 20 2 MouseLeftUp 20 2 // No MouseLeftClick event ``` The latest commit should result in this. If you want to process the click event, you need to let the "mouse up" event come through.
Author
Owner

@RasmusLindroth commented on GitHub (Aug 13, 2022):

Awesome and thank you :)

<!-- gh-comment-id:1214131969 --> @RasmusLindroth commented on GitHub (Aug 13, 2022): Awesome and thank you :)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tview#551
No description provided.