rivo / tview

Terminal UI library with rich, interactive widgets — written in Golang
MIT License
11.13k stars 575 forks source link

*.Box,SetMouseCapture() captures all mouse-events, not only those within the Box #926

Open FJuedesOrcl opened 11 months ago

FJuedesOrcl commented 11 months ago

Hi Friends,

i am not sure if this is intentional, by mistake or technically unavoidable:

Am i doing something wrong? - Please have a look at the code below. Another question arose while writing the short experimental code: How can one map the mouse-event coordinates to a TUI-element? I am using flexible-grid dimensions, so that the dimensions of the elements are depending on the size of the terminal window.

Any hints and recommendations very welcomed!

Best regards from Charleston (WV), Frank/2

Here is the sample code:

package main

import (
  "fmt"
  "github.com/gdamore/tcell/v2"
  "github.com/rivo/tview"
)

func main() {
    // Configure a status-line for debug output
    Status := tview.NewTextView().SetText("Statusline")

    // Configure a Mouse-Capture function       
    MouseCapture := func(action tview.MouseAction, event *tcell.EventMouse) (tview.MouseAction, *tcell.EventMouse) {
      // Print the Mouse-Action as a bitmap
      Output := fmt.Sprintf("MouseAction: %%%016b; ",action)

      // Print the Mouse Buttons as bitmap
      buttons := event.Buttons()
      Output += fmt.Sprintf("MouseButtons: %%%016b; ",buttons)

      // Print the modifiers as bitmap
      modifiers := event.Modifiers()
      Output += fmt.Sprintf("Modifier Keys: %%%016b; ",modifiers)

      // Print the event-position
      PosX,PosY := event.Position()
      Output += fmt.Sprintf("PosX: %03d; PosY: %02d",PosX,PosY)

      // "print" output into status-line
      Status.SetText(Output)

      return action,event
    } // END MouseCapture

    // Configure a table with selectable rows
    Panel := tview.NewTable()
    Panel.SetBorders(true)
    Panel.SetBorderColor(tcell.NewRGBColor(127,127,127))
    Panel.SetSelectable(true,false)
    for row:=0;row<13;row++ {
      for col:=0;col<6;col++ {
        Panel.SetCell(row,col,tview.NewTableCell(fmt.Sprintf("row: %2d; col: %2d",row,col)).SetSelectable(true))
      } // END for col
    } // End for row

    // Configure a TreeView based menu
    RootNode := tview.NewTreeNode("ROOT").SetColor(tcell.NewRGBColor(255,15,15)).SetSelectable(true)
    for n:=0;n<10;n++ {
      node := tview.NewTreeNode(fmt.Sprintf("Node: %d",n)).SetColor(tcell.NewRGBColor(15,255,15)).SetSelectable(true)
      RootNode.AddChild(node)
    } // END for n

    Tree := tview.NewTreeView().SetRoot(RootNode).SetCurrentNode(RootNode).
                                SetGraphics(true).SetGraphicsColor(tcell.NewRGBColor(15,15,255))

    // !!!!!!!!!! We set the MouseCapture for the TreeView.Box only !!!!!!!!!!
    Tree.Box.SetBorder(true).SetMouseCapture(MouseCapture)                            

    // Configure a grid with the TreeView to the left, the Table to the right and the status-line at the bottom
    Grid := tview.NewGrid()
    Grid.SetColumns(-1,-3).SetRows(0,1).
         AddItem(Tree,0,0,1,1,0,0,true).
         AddItem(Panel,0,1,1,1,0,0,false).
         AddItem(Status,1,0,1,2,0,0,false)

    // Create and run the application
    app := tview.NewApplication().EnableMouse(true).SetMouseCapture(nil)
    if err := app.SetRoot(Grid, true).Run(); err != nil {
        panic(err)
    } // END if
} // END main
digitallyserviced commented 11 months ago

@FJuedesOrcl

// SetMouseCapture sets a function which captures mouse events (consisting of
// the original tcell mouse event and the semantic mouse action) before they are
// forwarded to the primitive's default mouse event handler. This function can
// then choose to forward that event (or a different one) by returning it or
// returning a nil mouse event, in which case the default handler will not be
// called.

So this is how it works. The comment doc even states that it is meant to be a handler to choose whether or not the mouse event should be passed on to the primitives default mouse event handler, which is the one that is built into the TreeView primitive.

If you want to be able to handle more events then what the default primitive is capable of handling, then you need to think about creating a new primtive, or subclassing/embedding the treeview widget.

The wiki describes how to create your own primitive. THen you would be utilizing 'WrapMouseHandler' and 'WrapInputHandler' instead of the global Capture handler that is on the Box primitive.

Also what if you want to make a modal dialog box, and have the MouseCapture force all events to the modal, so it can detect if any clicks were made outside of it to dismiss it? It is just another feature, albeit probably not what you are looking for in this use case.