From cffa246f92566fa385373cee82349f84d7c28212 Mon Sep 17 00:00:00 2001 From: Richard Burke Date: Wed, 1 May 2019 20:58:05 +0100 Subject: Show bindings for each action in popup menu - The displayed bindings can be used to execute actions - User defined mappings are also displayed --- cmd/grv/commit_view.go | 1 + cmd/grv/config.go | 7 +-- cmd/grv/context_menu_view.go | 100 ++++++++++++++++++++++++++++++++++++++----- cmd/grv/key_bindings.go | 5 ++- cmd/grv/ref_view.go | 7 +-- cmd/grv/theme.go | 1 + cmd/grv/themes.go | 8 ++++ doc/documentation.md | 3 +- 8 files changed, 114 insertions(+), 18 deletions(-) diff --git a/cmd/grv/commit_view.go b/cmd/grv/commit_view.go index 87c3749..9e48e28 100644 --- a/cmd/grv/commit_view.go +++ b/cmd/grv/commit_view.go @@ -1016,6 +1016,7 @@ func showActionsForCommit(commitView *CommitView, action Action) (err error) { cols: 60, }, config: ContextMenuConfig{ + ActionView: ViewCommit, Entries: []ContextMenuEntry{ { DisplayName: "Checkout commit", diff --git a/cmd/grv/config.go b/cmd/grv/config.go index a808739..f53a179 100644 --- a/cmd/grv/config.go +++ b/cmd/grv/config.go @@ -222,9 +222,10 @@ var themeComponents = map[string]ThemeComponentID{ cfErrorView + ".Footer": CmpErrorViewFooter, cfErrorView + ".Errors": CmpErrorViewErrors, - cfContextMenuView + ".Title": CmpContextMenuTitle, - cfContextMenuView + ".Content": CmpContextMenuContent, - cfContextMenuView + ".Footer": CmpContextMenuFooter, + cfContextMenuView + ".Title": CmpContextMenuTitle, + cfContextMenuView + ".Content": CmpContextMenuContent, + cfContextMenuView + ".KeyMapping": CmpContextMenuKeyMapping, + cfContextMenuView + ".Footer": CmpContextMenuFooter, cfCommandOutputView + ".Title": CmpCommandOutputTitle, cfCommandOutputView + ".Command": CmpCommandOutputCommand, diff --git a/cmd/grv/context_menu_view.go b/cmd/grv/context_menu_view.go index aa3835b..46cbceb 100644 --- a/cmd/grv/context_menu_view.go +++ b/cmd/grv/context_menu_view.go @@ -21,9 +21,10 @@ type ContextMenuEntry struct { // ContextMenuConfig is the configuration for the ContextMenuView // It contains all context menu contextMenuConfig and an observer type ContextMenuConfig struct { - Entity string - Entries []ContextMenuEntry - OnSelect OnContextMenuEntrySelected + Entity string + Entries []ContextMenuEntry + OnSelect OnContextMenuEntrySelected + ActionView ViewID } type contextMenuViewHandler func(*ContextMenuView, Action) error @@ -51,16 +52,65 @@ func NewContextMenuView(contextMenuConfig ContextMenuConfig, channels Channels, } contextMenuView.AbstractWindowView = NewAbstractWindowView(contextMenuView, channels, config, variables, &contextMenuView.lock, "menu item") + contextMenuView.processConfig() + + return contextMenuView +} + +func (contextMenuView *ContextMenuView) processConfig() { + contextMenuConfig := &contextMenuView.contextMenuConfig if contextMenuConfig.Entity == "" { - contextMenuView.contextMenuConfig.Entity = "Action" + contextMenuConfig.Entity = "Action" } - return contextMenuView + if contextMenuConfig.ActionView != ViewAll { + var keys []string + maxKeyWidth := 0 + + for i := uint(0); i < contextMenuView.rows(); i++ { + entry := &contextMenuConfig.Entries[i] + var key string + + if action, ok := entry.Value.(Action); ok { + mappings := contextMenuView.config.KeyStrings(action.ActionType, ViewHierarchy{contextMenuConfig.ActionView}) + + if len(mappings) > 0 { + key = mappings[len(mappings)-1].keystring + } + } + + if key == "" { + key = "None" + } + + keys = append(keys, key) + + width := StringWidth(key) + if width > maxKeyWidth { + maxKeyWidth = width + } + } + + for keyIndex, key := range keys { + if width := StringWidth(key); width < maxKeyWidth { + key = fmt.Sprintf("%v%v", key, strings.Repeat(" ", maxKeyWidth-width)) + } + + entry := &contextMenuConfig.Entries[keyIndex] + entry.DisplayName = fmt.Sprintf("%v %v", key, entry.DisplayName) + } + } + + return } // ViewID returns the ViewID of the context menu view func (contextMenuView *ContextMenuView) ViewID() ViewID { + if contextMenuView.contextMenuConfig.ActionView != ViewAll { + return contextMenuView.contextMenuConfig.ActionView + } + return ViewContextMenu } @@ -80,18 +130,29 @@ func (contextMenuView *ContextMenuView) Render(win RenderWindow) (err error) { viewRowIndex := viewPos.ViewStartRowIndex() startColumn := viewPos.ViewStartColumn() - for rowIndex := uint(0); rowIndex < winRows && viewRowIndex < viewRows; rowIndex++ { - entry := contextMenuView.contextMenuConfig.Entries[viewRowIndex] + win.ApplyStyle(CmpContextMenuContent) - if err = win.SetRow(rowIndex+1, startColumn, CmpNone, " %v", entry.DisplayName); err != nil { + var lineBuilder *LineBuilder + for rowIndex := uint(0); rowIndex < winRows && viewRowIndex < viewRows; rowIndex++ { + if lineBuilder, err = win.LineBuilder(rowIndex+1, startColumn); err != nil { return } + entry := contextMenuView.contextMenuConfig.Entries[viewRowIndex] + if contextMenuView.contextMenuConfig.ActionView != ViewAll { + if displayParts := strings.SplitN(entry.DisplayName, " ", 2); len(displayParts) == 2 { + lineBuilder.AppendWithStyle(CmpContextMenuKeyMapping, " %v ", displayParts[0]). + AppendWithStyle(CmpContextMenuContent, "%v", displayParts[1]) + } else { + return fmt.Errorf(`Expected entry of format "key DisplayText" but found %v`, entry.DisplayName) + } + } else { + lineBuilder.AppendWithStyle(CmpContextMenuContent, " %v", entry.DisplayName) + } + viewRowIndex++ } - win.ApplyStyle(CmpContextMenuContent) - if err = win.SetSelectedRow(viewPos.SelectedRowIndex()+1, ViewStateActive); err != nil { return } @@ -159,6 +220,8 @@ func (contextMenuView *ContextMenuView) HandleAction(action Action) (err error) err = handler(contextMenuView, action) } else if handled, err = contextMenuView.AbstractWindowView.HandleAction(action); handled { log.Debugf("Action handled by AbstractWindowView") + } else if handled, err = contextMenuView.handleMenuAction(action); handled { + log.Debugf("Menu action handled") } else { log.Debugf("Action not handled") } @@ -166,6 +229,23 @@ func (contextMenuView *ContextMenuView) HandleAction(action Action) (err error) return } +func (contextMenuView *ContextMenuView) handleMenuAction(action Action) (handled bool, err error) { + if contextMenuView.contextMenuConfig.ActionView == ViewAll { + return + } + + for entryIndex, entry := range contextMenuView.contextMenuConfig.Entries { + if entryAction, ok := entry.Value.(Action); ok && entryAction.ActionType == action.ActionType { + contextMenuView.viewPos().SetActiveRowIndex(uint(entryIndex)) + err = selectContextMenuEntry(contextMenuView, Action{ActionType: ActionSelect}) + handled = true + break + } + } + + return +} + func selectContextMenuEntry(contextMenuView *ContextMenuView, action Action) (err error) { viewPos := contextMenuView.viewPos() selectedIndex := viewPos.ActiveRowIndex() diff --git a/cmd/grv/key_bindings.go b/cmd/grv/key_bindings.go index 8a9fdcb..b76dbb6 100644 --- a/cmd/grv/key_bindings.go +++ b/cmd/grv/key_bindings.go @@ -491,8 +491,11 @@ var actionDescriptors = map[ActionType]ActionDescriptor{ }, ActionCheckoutPreviousRef: { actionKey: "", - actionCategory: ActionCategoryGeneral, + actionCategory: ActionCategoryViewSpecific, description: "Checkout previous ref", + keyBindings: map[ViewID][]string{ + ViewRef: {"-"}, + }, }, ActionCheckoutCommit: { actionKey: "", diff --git a/cmd/grv/ref_view.go b/cmd/grv/ref_view.go index 68cfa5f..7197d48 100644 --- a/cmd/grv/ref_view.go +++ b/cmd/grv/ref_view.go @@ -1362,8 +1362,8 @@ func showActionsForRef(refView *RefView, action Action) (err error) { head := refView.repoData.Head() headName := head.Shorthand() - if StringWidth(headName) > 15 { - headName = headName[:15] + "..." + if StringWidth(headName) > 12 { + headName = headName[:12] + "..." } if !isHead { @@ -1393,7 +1393,8 @@ func showActionsForRef(refView *RefView, action Action) (err error) { cols: 60, }, config: ContextMenuConfig{ - Entries: contextMenuEntries, + ActionView: ViewRef, + Entries: contextMenuEntries, OnSelect: func(entry ContextMenuEntry, entryIndex uint) { if selectedAction, ok := entry.Value.(Action); ok { refView.channels.DoAction(selectedAction) diff --git a/cmd/grv/theme.go b/cmd/grv/theme.go index ce0d149..9a239b6 100644 --- a/cmd/grv/theme.go +++ b/cmd/grv/theme.go @@ -105,6 +105,7 @@ const ( CmpContextMenuTitle CmpContextMenuContent + CmpContextMenuKeyMapping CmpContextMenuFooter CmpCommandOutputTitle diff --git a/cmd/grv/themes.go b/cmd/grv/themes.go index 48db135..64d3c31 100644 --- a/cmd/grv/themes.go +++ b/cmd/grv/themes.go @@ -311,6 +311,10 @@ func NewClassicTheme() MutableTheme { bgcolor: NewSystemColor(ColorNone), fgcolor: NewSystemColor(ColorNone), }, + CmpContextMenuKeyMapping: { + bgcolor: NewSystemColor(ColorNone), + fgcolor: NewSystemColor(ColorBlue), + }, CmpContextMenuFooter: { bgcolor: NewSystemColor(ColorNone), fgcolor: NewSystemColor(ColorCyan), @@ -795,6 +799,10 @@ func NewSolarizedTheme() MutableTheme { bgcolor: NewColorNumber(solarizedBlack), fgcolor: NewColorNumber(solarizedWhite), }, + CmpContextMenuKeyMapping: { + bgcolor: NewColorNumber(solarizedBlack), + fgcolor: NewColorNumber(solarizedBlue), + }, CmpContextMenuFooter: { bgcolor: NewColorNumber(solarizedBlack), fgcolor: NewColorNumber(solarizedCyan), diff --git a/doc/documentation.md b/doc/documentation.md index c510c4d..d6df5c2 100644 --- a/doc/documentation.md +++ b/doc/documentation.md @@ -125,7 +125,6 @@ The following tables contain default and user configured key bindings ``` Key Bindings | Action | Description -------------+------------------------------+-------------------------------------------------- - None | | Checkout previous ref None | | Exit GRV : | | GRV Command prompt None | | Remove the active tab @@ -140,6 +139,7 @@ The following tables contain default and user configured key bindings ``` Key Bindings | Action | Description -------------+----------------------------------+-------------------------------------------- + - | | Checkout previous ref c | | Checkout ref T | | Create a new annotated tag B | | Create a new branch and checkout @@ -590,6 +590,7 @@ CommitView.Title ContextMenuView.Content ContextMenuView.Footer +ContextMenuView.KeyMapping ContextMenuView.Title DiffView.AddedLine -- cgit v1.2.3