[#1223] lens/tui: Add app help
Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
parent
ed396448ac
commit
e655336390
4 changed files with 179 additions and 1 deletions
38
cmd/frostfs-lens/internal/tui/help-pages/hotkeys.txt
Normal file
38
cmd/frostfs-lens/internal/tui/help-pages/hotkeys.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
[green::b]HOTKEYS[-::-]
|
||||||
|
|
||||||
|
[green::b]Navigation[-::-]
|
||||||
|
|
||||||
|
[yellow::b]Down Arrow[-::-] / [yellow::b]j[-::-]
|
||||||
|
Scroll down.
|
||||||
|
|
||||||
|
[yellow::b]Up Arrow[-::-] / [yellow::b]k[-::-]
|
||||||
|
Scroll up.
|
||||||
|
|
||||||
|
[yellow::b]Page Down[-::-] / [yellow::b]Ctrl-f[-::-]
|
||||||
|
Scroll down by a full page.
|
||||||
|
|
||||||
|
[yellow::b]Page Up[-::-] / [yellow::b]Ctrl-b[-::-]
|
||||||
|
Scroll up by a full page.
|
||||||
|
|
||||||
|
[green::b]Actions[-::-]
|
||||||
|
|
||||||
|
[yellow::b]Enter[-::-]
|
||||||
|
Perform actions based on the current context:
|
||||||
|
- In Buckets View:
|
||||||
|
- Expand/collapse the selected bucket to show/hide its nested buckets.
|
||||||
|
- If no nested buckets exist, navigate to the selected bucket's records.
|
||||||
|
- In Records View: Open the detailed view of the selected record.
|
||||||
|
|
||||||
|
[yellow::b]Escape[-::-]
|
||||||
|
Return to the previous page, opposite of [yellow::b]Enter[-::-].
|
||||||
|
|
||||||
|
Refer to the [green::b]SEARCHING[-::-] section for more specific actions.
|
||||||
|
|
||||||
|
|
||||||
|
[green::b]Alternative Action Hotkeys[-::-]
|
||||||
|
|
||||||
|
[yellow::b]Ctrl-r[-::-]
|
||||||
|
Directly navigate to the selected bucket's records.
|
||||||
|
|
||||||
|
[yellow::b]Ctrl-d[-::-]
|
||||||
|
Access the detailed view of the selected bucket.
|
26
cmd/frostfs-lens/internal/tui/help-pages/searching.txt
Normal file
26
cmd/frostfs-lens/internal/tui/help-pages/searching.txt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[green::b]SEARCHING[-::-]
|
||||||
|
|
||||||
|
[green::b]Hotkeys[-::-]
|
||||||
|
|
||||||
|
[yellow::b]/[-::-]
|
||||||
|
Initiate the search prompt.
|
||||||
|
- The prompt follows this syntax: [yellow::b]tag:value [+ tag:value]...[-::-]
|
||||||
|
- Multiple filter can be combined with [yellow::b]+[-::-], the result is an intersection of those filters' result sets.
|
||||||
|
- Any leading and trailing whitespace will be ignored.
|
||||||
|
- An empty prompt will return all results with no filters applied.
|
||||||
|
- Refer to the [green::b]Available Search Filters[-::-] section below for a list of valid filter tags.
|
||||||
|
|
||||||
|
[yellow::b]Enter[-::-]
|
||||||
|
Execute the search based on the entered prompt.
|
||||||
|
- If the prompt is invalid, an error message will be displayed.
|
||||||
|
|
||||||
|
[yellow::b]Escape[-::-]
|
||||||
|
Exit the search prompt without performing a search.
|
||||||
|
|
||||||
|
[yellow::b]Down Arrow[-::-], [yellow::b]Up Arrow[-::-]
|
||||||
|
Scroll through the search history.
|
||||||
|
|
||||||
|
|
||||||
|
[green::b]Available Search Filters[-::-]
|
||||||
|
|
||||||
|
%s
|
101
cmd/frostfs-lens/internal/tui/help.go
Normal file
101
cmd/frostfs-lens/internal/tui/help.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed help-pages/hotkeys.txt
|
||||||
|
hotkeysHelpText string
|
||||||
|
|
||||||
|
//go:embed help-pages/searching.txt
|
||||||
|
searchingHelpText string
|
||||||
|
)
|
||||||
|
|
||||||
|
type HelpPage struct {
|
||||||
|
*tview.Box
|
||||||
|
pages []*tview.TextView
|
||||||
|
currentPage int
|
||||||
|
|
||||||
|
filters []string
|
||||||
|
filterHints map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHelpPage(filters []string, hints map[string]string) *HelpPage {
|
||||||
|
hp := &HelpPage{
|
||||||
|
Box: tview.NewBox(),
|
||||||
|
filters: filters,
|
||||||
|
filterHints: hints,
|
||||||
|
}
|
||||||
|
|
||||||
|
page := tview.NewTextView().
|
||||||
|
SetDynamicColors(true).
|
||||||
|
SetText(hotkeysHelpText)
|
||||||
|
hp.addPage(page)
|
||||||
|
|
||||||
|
page = tview.NewTextView().
|
||||||
|
SetDynamicColors(true).
|
||||||
|
SetText(fmt.Sprintf(searchingHelpText, hp.getFiltersText()))
|
||||||
|
hp.addPage(page)
|
||||||
|
|
||||||
|
return hp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HelpPage) addPage(page *tview.TextView) {
|
||||||
|
hp.pages = append(hp.pages, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HelpPage) getFiltersText() string {
|
||||||
|
if len(hp.filters) == 0 {
|
||||||
|
return "\t\tNo filters defined.\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
filtersText := strings.Builder{}
|
||||||
|
gapSize := 4
|
||||||
|
|
||||||
|
tagMaxWidth := 3
|
||||||
|
for _, filter := range hp.filters {
|
||||||
|
tagMaxWidth = max(tagMaxWidth, len(filter))
|
||||||
|
}
|
||||||
|
filtersText.WriteString("\t\t[yellow::b]Tag")
|
||||||
|
filtersText.WriteString(strings.Repeat(" ", gapSize))
|
||||||
|
filtersText.WriteString("\tValue[-::-]\n\n")
|
||||||
|
|
||||||
|
for _, filter := range hp.filters {
|
||||||
|
filtersText.WriteString("\t\t")
|
||||||
|
filtersText.WriteString(filter)
|
||||||
|
filtersText.WriteString(strings.Repeat(" ", tagMaxWidth-len(filter)+gapSize))
|
||||||
|
filtersText.WriteString(hp.filterHints[filter])
|
||||||
|
filtersText.WriteRune('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtersText.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HelpPage) Draw(screen tcell.Screen) {
|
||||||
|
x, y, width, height := hp.GetInnerRect()
|
||||||
|
hp.pages[hp.currentPage].SetRect(x+1, y+1, width-2, height-2)
|
||||||
|
hp.pages[hp.currentPage].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)) {
|
||||||
|
if event.Key() == tcell.KeyEnter {
|
||||||
|
hp.currentPage++
|
||||||
|
hp.currentPage %= len(hp.pages)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hp.pages[hp.currentPage].InputHandler()(event, func(tview.Primitive) {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp *HelpPage) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
||||||
|
return hp.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, _ func(tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
||||||
|
return hp.pages[hp.currentPage].MouseHandler()(action, event, func(tview.Primitive) {})
|
||||||
|
})
|
||||||
|
}
|
|
@ -60,6 +60,8 @@ type UI struct {
|
||||||
loadingBar *LoadingBar
|
loadingBar *LoadingBar
|
||||||
helpBar *tview.TextView
|
helpBar *tview.TextView
|
||||||
|
|
||||||
|
helpPage *HelpPage
|
||||||
|
|
||||||
searchErrorBar *tview.TextView
|
searchErrorBar *tview.TextView
|
||||||
|
|
||||||
isSearching bool
|
isSearching bool
|
||||||
|
@ -275,7 +277,17 @@ func (ui *UI) draw(screen tcell.Screen) {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case ui.isShowingHelp:
|
case ui.isShowingHelp:
|
||||||
pageToDraw = ui.pageStub
|
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
|
||||||
case ui.mountedPage != nil:
|
case ui.mountedPage != nil:
|
||||||
pageToDraw = ui.mountedPage
|
pageToDraw = ui.mountedPage
|
||||||
default:
|
default:
|
||||||
|
@ -429,6 +441,7 @@ func (ui *UI) handleInputOnShowingHelp(event *tcell.EventKey) {
|
||||||
case k == tcell.KeyRune && r == 'q':
|
case k == tcell.KeyRune && r == 'q':
|
||||||
ui.stop()
|
ui.stop()
|
||||||
default:
|
default:
|
||||||
|
ui.helpPage.InputHandler()(event, func(tview.Primitive) {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue