Added mouse support for Table and TreeView.

This commit is contained in:
Oliver 2020-03-27 21:13:03 +01:00
parent 9af6826328
commit 5f5b79b00e
6 changed files with 115 additions and 10 deletions

View File

@ -1,6 +1,6 @@
# Rich Interactive Widgets for Terminal UIs
[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/rivo/tview)
[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/rivo/tview)
[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/tview)
This Go package provides commonly needed components for terminal based user interfaces.

View File

@ -39,7 +39,7 @@ func main() {
table.GetCell(row, column).SetTextColor(tcell.ColorRed)
table.SetSelectable(false, false)
})
if err := app.SetRoot(table, true).Run(); err != nil {
if err := app.SetRoot(table, true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

View File

@ -56,7 +56,7 @@ func main() {
}
})
if err := tview.NewApplication().SetRoot(tree, true).Run(); err != nil {
if err := tview.NewApplication().SetRoot(tree, true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

View File

@ -419,8 +419,7 @@ func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
})
}
// evalPrefix is selects an item in the drop-down list based on the current
// prefix.
// evalPrefix selects an item in the drop-down list based on the current prefix.
func (d *DropDown) evalPrefix() {
if len(d.prefix) > 0 {
for index, option := range d.options {
@ -430,7 +429,7 @@ func (d *DropDown) evalPrefix() {
}
}
// Prefix does not match any item. Remove last rune. TODO: Use uniseg here.
// Prefix does not match any item. Remove last rune.
r := []rune(d.prefix)
d.prefix = string(r[:len(r)-1])
}

View File

@ -251,6 +251,13 @@ type Table struct {
// The number of visible rows the last time the table was drawn.
visibleRows int
// The indices of the visible columns as of the last time the table was drawn.
visibleColumnIndices []int
// The net widths of the visible columns as of the last time the table was
// drawn.
visibleColumnWidths []int
// The style of the selected rows. If this value is 0, selected rows are
// simply inverted.
selectedStyle tcell.Style
@ -360,8 +367,8 @@ func (t *Table) GetSelection() (row, column int) {
// Select sets the selected cell. Depending on the selection settings
// specified via SetSelectable(), this may be an entire row or column, or even
// ignored completely. The "selection changed" event is fired if such a callback
// is available (even if the selection ends up being the same as before, even if
// cells are not selectable).
// is available (even if the selection ends up being the same as before and even
// if cells are not selectable).
func (t *Table) Select(row, column int) *Table {
t.selectedRow, t.selectedColumn = row, column
if t.selectionChanged != nil {
@ -537,6 +544,49 @@ func (t *Table) GetColumnCount() int {
return t.lastColumn + 1
}
// cellAt returns the row and column located at the given screen coordinates.
// Each returned value may be negative if there is no row and/or cell. This
// function will also process coordinates outside the table's inner rectangle so
// callers will need to check for bounds themselves.
func (t *Table) cellAt(x, y int) (row, column int) {
rectX, rectY, _, _ := t.GetInnerRect()
// Determine row as seen on screen.
if t.borders {
row = (y - rectY - 1) / 2
} else {
row = y - rectY
}
// Respect fixed rows and row offset.
if row >= 0 {
if row >= t.fixedRows {
row += t.rowOffset
}
if row >= len(t.cells) {
row = -1
}
}
// Saerch for the clicked column.
column = -1
if x >= rectX {
columnX := rectX
if t.borders {
columnX++
}
for index, width := range t.visibleColumnWidths {
columnX += width + 1
if x < columnX {
column = t.visibleColumnIndices[index]
break
}
}
}
return
}
// ScrollToBeginning scrolls the table to the beginning to that the top left
// corner of the table is shown. Note that this position may be corrected if
// there is a selection.
@ -824,8 +874,8 @@ ColumnLoop:
cell.x, cell.y, cell.width = x+columnX+1, y+rowY, finalWidth
_, printed := printWithStyle(screen, cell.Text, x+columnX+1, y+rowY, finalWidth, cell.Align, tcell.StyleDefault.Foreground(cell.Color)|tcell.Style(cell.Attributes))
if TaggedStringWidth(cell.Text)-printed > 0 && printed > 0 {
_, _, style, _ := screen.GetContent(x+columnX+1+finalWidth-1, y+rowY)
printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+1+finalWidth-1, y+rowY, 1, AlignLeft, style)
_, _, style, _ := screen.GetContent(x+columnX+finalWidth, y+rowY)
printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+finalWidth, y+rowY, 1, AlignLeft, style)
}
}
@ -964,6 +1014,9 @@ ColumnLoop:
}
}
}
// Remember column infos.
t.visibleColumnIndices, t.visibleColumnWidths = columns, widths
}
// InputHandler returns the handler for this primitive.
@ -1173,3 +1226,24 @@ func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primi
}
})
}
// MouseHandler returns the mouse handler for this primitive.
func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
x, y := event.Position()
if !t.InRect(x, y) {
return false, nil
}
switch action {
case MouseLeftClick:
if t.rowsSelectable || t.columnsSelectable {
t.Select(t.cellAt(x, y))
}
consumed = true
setFocus(t)
}
return
})
}

View File

@ -730,3 +730,35 @@ func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
t.process()
})
}
// MouseHandler returns the mouse handler for this primitive.
func (t *TreeView) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
x, y := event.Position()
if !t.InRect(x, y) {
return false, nil
}
switch action {
case MouseLeftClick:
_, rectY, _, _ := t.GetInnerRect()
y -= rectY
if y >= 0 && y < len(t.nodes) {
node := t.nodes[y]
if node.selectable {
if t.currentNode != node && t.changed != nil {
t.changed(node)
}
if t.selected != nil {
t.selected(node)
}
t.currentNode = node
}
}
consumed = true
setFocus(t)
}
return
})
}