diff --git a/internal/ui/downloads.go b/internal/ui/downloads.go index 29efb8f..efe3ff6 100644 --- a/internal/ui/downloads.go +++ b/internal/ui/downloads.go @@ -293,10 +293,14 @@ func fill(t *tview.Table, expand int, rows [][]string) { // shows placeholder text when there are no rows. func restoreSelection(t *tview.Table, n, selRow int, placeholder string) { if n == 0 { + // With no selectable row, tview's Down/Up handling never terminates, + // so turn selection off entirely. t.SetCell(1, 0, tview.NewTableCell("[gray]"+placeholder).SetSelectable(false)) + t.SetSelectable(false, false) t.Select(0, 0) return } + t.SetSelectable(true, false) t.Select(max(selRow, 1), 0) } diff --git a/internal/ui/releases.go b/internal/ui/releases.go index 555b0fa..aafab92 100644 --- a/internal/ui/releases.go +++ b/internal/ui/releases.go @@ -166,6 +166,8 @@ func (v *releasesView) render() { } v.table.SetCell(1, 1, tview.NewTableCell("[gray]"+msg).SetSelectable(false)) } + // With no selectable row tview's Down/Up handling never terminates. + v.table.SetSelectable(len(v.shown) > 0, false) if len(v.shown) > 0 { v.table.Select(max(selRow, 1), 0) } else { diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go new file mode 100644 index 0000000..8ada251 --- /dev/null +++ b/internal/ui/table_test.go @@ -0,0 +1,38 @@ +package ui + +import ( + "testing" + "time" + + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +// Pressing Down in an empty table used to loop forever in tview because the +// placeholder row is not selectable. +func TestEmptyTableDownDoesNotHang(t *testing.T) { + tbl := tview.NewTable().SetSelectable(true, false).SetFixed(1, 0) + restoreSelection(tbl, 0, 0, "empty") + + screen := tcell.NewSimulationScreen("") + if err := screen.Init(); err != nil { + t.Fatal(err) + } + defer screen.Fini() + screen.SetSize(80, 10) + tbl.SetRect(0, 0, 80, 10) + + done := make(chan struct{}) + go func() { + tbl.Draw(screen) + tbl.InputHandler()(tcell.NewEventKey(tcell.KeyDown, 0, 0), func(tview.Primitive) {}) + tbl.InputHandler()(tcell.NewEventKey(tcell.KeyUp, 0, 0), func(tview.Primitive) {}) + tbl.Draw(screen) + close(done) + }() + select { + case <-done: + case <-time.After(2 * time.Second): + t.Fatal("table input handler hung on an empty table") + } +}