[#1223] lens/tui: Add help page
Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
parent
1504a1ae19
commit
bcd48ff221
3 changed files with 112 additions and 18 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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) {})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
|
|
Loading…
Reference in a new issue