diff --git a/cmd/frostfs-lens/internal/tuiutil/help-pages/development.txt b/cmd/frostfs-lens/internal/tuiutil/help-pages/development.txt new file mode 100644 index 000000000..97e0e6668 --- /dev/null +++ b/cmd/frostfs-lens/internal/tuiutil/help-pages/development.txt @@ -0,0 +1 @@ +[green::b]Development[white::-] (Work in Progress) diff --git a/cmd/frostfs-lens/internal/tuiutil/help-pages/navigation.txt b/cmd/frostfs-lens/internal/tuiutil/help-pages/navigation.txt new file mode 100644 index 000000000..223fb8135 --- /dev/null +++ b/cmd/frostfs-lens/internal/tuiutil/help-pages/navigation.txt @@ -0,0 +1,27 @@ +[green::b]General Navigation[white::-] + + [yellow::b]Down Arrow[white::-] / [yellow::b]j[white::-] + Scroll down. + + [yellow::b]Up Arrow[white::-] / [yellow::b]k[white::-] + Scroll up. + + [yellow::b]Page Down[white::-] / [yellow::b]Ctrl-f[white::-] + Scroll down by a full page. + + [yellow::b]Page Up[white::-] / [yellow::b]Ctrl-b[white::-] + Scroll up by a full page. + + [yellow::b]Enter[white::-] + Navigate to a nested view: + - In Buckets View: Navigate to the selected bucket's records. + - In Records View: Navigate to the selected record's detailed view. + + [yellow::b]Escape[white::-] + Return to the previous page, opposite of [yellow::b]Enter[white::-]. + + +[green::b]Buckets View Interactions[white::-] + + [yellow::b]Space[white::-] + Expand or collapse the selected bucket to show or hide its nested buckets. diff --git a/cmd/frostfs-lens/internal/tuiutil/help-pages/searching.txt b/cmd/frostfs-lens/internal/tuiutil/help-pages/searching.txt new file mode 100644 index 000000000..6bc322f40 --- /dev/null +++ b/cmd/frostfs-lens/internal/tuiutil/help-pages/searching.txt @@ -0,0 +1,16 @@ +[green::b]Searching[white::-] + + [yellow::b]/[white::-] + Initiate a search prompt. Prompt syntax: [yellow::b]:[white::-]. Leading and trailing white spaces are ignored. See [green::b]Available Search Filters[white::-] section for available filter tags. + + [yellow::b]Enter[white::-] + Execute the search based on the entered prompt. An error message will display if the prompt is invalid. + + [yellow::b]Escape[white::-] + Exit the search prompt. + + +[green::b]Available Search Filters[white::-] + +%s + If you want to define a new filter, see [green::b]Development[white::-] section. diff --git a/cmd/frostfs-lens/internal/tuiutil/help.go b/cmd/frostfs-lens/internal/tuiutil/help.go index 4f4dee99f..97e8ace91 100644 --- a/cmd/frostfs-lens/internal/tuiutil/help.go +++ b/cmd/frostfs-lens/internal/tuiutil/help.go @@ -1,6 +1,7 @@ package tuiutil import ( + _ "embed" "fmt" "strings" @@ -8,76 +9,101 @@ import ( "github.com/rivo/tview" ) -const text = `[green]Navigation[white] +var ( + //go:embed help-pages/navigation.txt + navigationHelpText string -- [yellow]Up arrow[white] Move up. + //go:embed help-pages/searching.txt + searchingHelpText string -- [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 :. - 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 -` + //go:embed help-pages/development.txt + developmentHelpText string +) type HelpPage struct { *tview.Box - view *tview.TextView + 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(), - view: tview.NewTextView(), + Box: tview.NewBox(), + filters: filters, + filterHints: hints, } - filtersText := strings.Builder{} + page := tview.NewTextView() + page.SetDynamicColors(true) + page.SetText(navigationHelpText) + hp.addPage(page) - 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') - } - } + page = tview.NewTextView() + page.SetDynamicColors(true) + page.SetText(fmt.Sprintf(searchingHelpText, hp.getFiltersText())) + hp.addPage(page) - hp.view.SetDynamicColors(true) - hp.view.SetText(fmt.Sprintf(text, filtersText.String())) + page = tview.NewTextView() + page.SetDynamicColors(true) + page.SetText(developmentHelpText) + 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 "\tNo filters defined.\n" + } + + filtersText := strings.Builder{} + gapSize := 4 + + tagMaxWidth := 3 + for _, filter := range hp.filters { + tagMaxWidth = max(tagMaxWidth, len(filter)) + } + filtersText.WriteString("\t[yellow::b]Tag") + filtersText.WriteString(strings.Repeat(" ", gapSize)) + filtersText.WriteString("\tArgs[white::-]\n\n") + + for _, filter := range hp.filters { + filtersText.WriteRune('\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.view.SetRect(x, y, width, height) - hp.view.Draw(screen) + 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)) { - hp.view.InputHandler()(event, 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) {}) }) } diff --git a/cmd/frostfs-lens/internal/tuiutil/ui.go b/cmd/frostfs-lens/internal/tuiutil/ui.go index 81ddcc8b2..8aec22c24 100644 --- a/cmd/frostfs-lens/internal/tuiutil/ui.go +++ b/cmd/frostfs-lens/internal/tuiutil/ui.go @@ -102,7 +102,7 @@ func NewUI(ctx context.Context, app *tview.Application, db *bbolt.DB) *UI { ui.helpBar.SetBackgroundColor(barBackgroundColor) ui.helpBar.SetTextColor(barTextColor) - ui.helpBar.SetText(" Press Escape to quit") + ui.helpBar.SetText(" Press Enter for next page or Escape to exit help ") ui.searchErrorBar.SetBackgroundColor(barTextColor) ui.searchErrorBar.SetTextColor(barAlertTextColor)