[#1223] lens/tui: Add help page

Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
Aleksey Savchuk 2024-08-12 09:28:00 +03:00
parent 1504a1ae19
commit bcd48ff221
No known key found for this signature in database
3 changed files with 112 additions and 18 deletions

View file

@ -41,10 +41,10 @@ func runTUI(cmd *cobra.Command) error {
app := tview.NewApplication()
ui := tuiutil.NewUI(ctx, app, db)
_ = ui.AddFilter("cid", cidParser)
_ = ui.AddFilter("oid", oidParser)
_ = ui.AddCompositeFilter("addr", addrParser)
_ = ui.AddCompositeFilter("attr", attributeParser)
_ = ui.AddFilter("cid", cidParser, "<CID>")
_ = ui.AddFilter("oid", oidParser, "<OID>")
_ = ui.AddCompositeFilter("addr", addrParser, "<CID>/<OID>")
_ = ui.AddCompositeFilter("attr", attributeParser, "<key> or <key>/<value>")
app.SetRoot(ui, true).SetFocus(ui)

View file

@ -1 +1,83 @@
package tuiutil
import (
"fmt"
"strings"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
const text = `[green]Navigation[white]
- [yellow]Up arrow[white] Move up.
- [yellow]Down arrow[white] Move down.
- [yellow]Space[white] Show a bucket's subbuckets if on the buckets view.
- [yellow]Enter[white] Show a bucket's records if on the buckets view,
or show a record's detailed view if on the
records view. It always navigates to a new page.
- [yellow]Escape[white] Go to the previous page.
[green]Searching[white]
- [yellow]/[white] Start search prompt.
Prompt syntax is <filter tag>:<filter args...>.
It tolerates leading and trailing white space.
- [yellow]Enter[white] Start searching based on prompt. It shows an
error message if the prompt is invalid.
- [yellow]Escape[white] Stop search prompt.
[green]Available search filters[white]
%s
`
type HelpPage struct {
*tview.Box
view *tview.TextView
}
func NewHelpPage(filters []string, hints map[string]string) *HelpPage {
hp := &HelpPage{
Box: tview.NewBox(),
view: tview.NewTextView(),
}
filtersText := strings.Builder{}
lastIndex := len(filters) - 1
for index, filter := range filters {
filtersText.WriteString("- ")
filtersText.WriteString(filter)
filtersText.WriteRune(' ')
filtersText.WriteString(hints[filter])
if index != lastIndex {
filtersText.WriteRune('\n')
}
}
hp.view.SetDynamicColors(true)
hp.view.SetText(fmt.Sprintf(text, filtersText.String()))
return hp
}
func (hp *HelpPage) Draw(screen tcell.Screen) {
x, y, width, height := hp.GetInnerRect()
hp.view.SetRect(x, y, width, height)
hp.view.Draw(screen)
}
func (hp *HelpPage) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return hp.WrapInputHandler(func(event *tcell.EventKey, _ func(tview.Primitive)) {
hp.view.InputHandler()(event, func(tview.Primitive) {})
})
}

View file

@ -47,8 +47,9 @@ type UI struct {
infoBar *tview.TextView
searchBar *tview.InputField
loadingBar *LoadingBar
helpBar *tview.TextView
helpPage *tview.TextView
helpPage *HelpPage
searchErrorBar *tview.TextView
@ -64,6 +65,7 @@ type UI struct {
filters map[string]func(string) (any, error)
compositeFilters map[string]func(string) (map[string]any, error)
filterHints map[string]string
}
func NewUI(ctx context.Context, app *tview.Application, db *bbolt.DB) *UI {
@ -77,8 +79,9 @@ func NewUI(ctx context.Context, app *tview.Application, db *bbolt.DB) *UI {
isFirstMount: true,
infoBar: tview.NewTextView(),
pageStub: tview.NewBox(),
helpPage: tview.NewTextView(),
searchBar: tview.NewInputField(),
filterHints: make(map[string]string),
helpBar: tview.NewTextView(),
searchErrorBar: tview.NewTextView(),
filters: make(map[string]func(string) (any, error)),
compositeFilters: make(map[string]func(string) (map[string]any, error)),
@ -97,14 +100,16 @@ func NewUI(ctx context.Context, app *tview.Application, db *bbolt.DB) *UI {
ui.infoBar.SetBackgroundColor(barBackgroundColor)
ui.infoBar.SetTextColor(barTextColor)
ui.helpBar.SetBackgroundColor(barBackgroundColor)
ui.helpBar.SetTextColor(barTextColor)
ui.helpBar.SetText(" Press Escape to quit")
ui.searchErrorBar.SetBackgroundColor(barTextColor)
ui.searchErrorBar.SetTextColor(barAlertTextColor)
ui.loadingBar = NewLoadingBar(ui.triggerDraw)
ui.pageToMount = NewBucketsView(ui, NewFilter(nil))
ui.helpPage.SetText("Work In Progress【ツ】")
ui.searchBar.SetLabel("/")
ui.infoBar.SetText(fmt.Sprintf(" %s (press h for help, / to search or q to quit) ", db.Path()))
@ -124,22 +129,26 @@ func (ui *UI) checkFilterExists(typ string) bool {
func (ui *UI) AddFilter(
typ string,
parser func(string) (any, error),
helpHint string,
) error {
if ui.checkFilterExists(typ) {
return fmt.Errorf("filter %s already exists", typ)
}
ui.filters[typ] = parser
ui.filterHints[typ] = helpHint
return nil
}
func (ui *UI) AddCompositeFilter(
typ string,
parser func(string) (map[string]any, error),
helpHint string,
) error {
if ui.checkFilterExists(typ) {
return fmt.Errorf("filter %s already exists", typ)
}
ui.compositeFilters[typ] = parser
ui.filterHints[typ] = helpHint
return nil
}
@ -222,26 +231,27 @@ func (ui *UI) draw(screen tcell.Screen) {
var (
pageToDraw tview.Primitive
barToDraw tview.Primitive
needDrawBar = true
)
switch {
case ui.isShowingHelp:
if ui.helpPage == nil {
var filters []string
for f := range ui.filters {
filters = append(filters, f)
}
for f := range ui.compositeFilters {
filters = append(filters, f)
}
ui.helpPage = NewHelpPage(filters, ui.filterHints)
}
pageToDraw = ui.helpPage
needDrawBar = false
case ui.mountedPage != nil:
pageToDraw = ui.mountedPage
default:
pageToDraw = ui.pageStub
}
if !needDrawBar {
pageToDraw.SetRect(x, y, width, height)
pageToDraw.Draw(screen)
return
}
pageToDraw.SetRect(x, y, width, height-1)
pageToDraw.Draw(screen)
@ -252,6 +262,8 @@ func (ui *UI) draw(screen tcell.Screen) {
barToDraw = ui.searchBar
case ui.isShowingError:
barToDraw = ui.searchErrorBar
case ui.isShowingHelp:
barToDraw = ui.helpBar
default:
barToDraw = ui.infoBar
}
@ -447,7 +459,7 @@ func (ui *UI) processPrompt(prompt string) (filter *Filter, err error) {
parts := strings.Split(prompt, ":")
if len(parts) != 2 {
return nil, errors.New("invalid syntax, expected <filter id>:<filter value>")
return nil, errors.New("invalid syntax, expected <filter id>:<filter args...>")
}
filterID := strings.TrimSpace(parts[0])