cview/contextmenu.go

177 lines
3.6 KiB
Go

package cview
import "sync"
// ContextMenu is a menu that appears upon user interaction, such as right
// clicking or pressing Alt+Enter.
type ContextMenu struct {
parent Primitive
item int
open bool
drag bool
list *List
x, y int
selected func(int, string, rune)
l sync.Mutex
}
// NewContextMenu returns a new context menu.
func NewContextMenu(parent Primitive) *ContextMenu {
return &ContextMenu{
parent: parent,
}
}
func (c *ContextMenu) initializeList() {
if c.list != nil {
return
}
c.list = NewList().
ShowSecondaryText(false).
SetHover(true).
SetWrapAround(true)
c.list.
ShowFocus(false).
SetBorder(true).
SetBorderPadding(
Styles.ContextMenuPaddingTop,
Styles.ContextMenuPaddingBottom,
Styles.ContextMenuPaddingLeft,
Styles.ContextMenuPaddingRight)
}
// ContextMenuList returns the underlying List of the context menu.
func (c *ContextMenu) ContextMenuList() *List {
c.l.Lock()
defer c.l.Unlock()
c.initializeList()
return c.list
}
// AddContextItem adds an item to the context menu. Adding an item with no text
// or shortcut will add a divider.
func (c *ContextMenu) AddContextItem(text string, shortcut rune, selected func(index int)) *ContextMenu {
c.l.Lock()
defer c.l.Unlock()
c.initializeList()
c.list.AddItem(NewListItem(text).SetShortcut(shortcut).SetSelectedFunc(c.wrap(selected)))
if text == "" && shortcut == 0 {
c.list.Lock()
index := len(c.list.items) - 1
c.list.items[index].enabled = false
c.list.Unlock()
}
return c
}
func (c *ContextMenu) wrap(f func(index int)) func() {
return func() {
f(c.item)
}
}
// ClearContextMenu removes all items from the context menu.
func (c *ContextMenu) ClearContextMenu() *ContextMenu {
c.l.Lock()
defer c.l.Unlock()
c.initializeList()
c.list.Clear()
return c
}
// SetContextSelectedFunc sets the function which is called when the user
// selects a context menu item. The function receives the item's index in the
// menu (starting with 0), its text and its shortcut rune. SetSelectedFunc must
// be called before the context menu is shown.
func (c *ContextMenu) SetContextSelectedFunc(handler func(index int, text string, shortcut rune)) *ContextMenu {
c.l.Lock()
defer c.l.Unlock()
c.selected = handler
return c
}
// ShowContextMenu shows the context menu. Provide -1 for both to position on
// the selected item, or specify a position.
func (c *ContextMenu) ShowContextMenu(item int, x int, y int, setFocus func(Primitive)) {
c.l.Lock()
defer c.l.Unlock()
c.show(item, x, y, setFocus)
}
// HideContextMenu hides the context menu.
func (c *ContextMenu) HideContextMenu(setFocus func(Primitive)) {
c.l.Lock()
defer c.l.Unlock()
c.hide(setFocus)
}
// ContextMenuVisible returns whether or not the context menu is visible.
func (c *ContextMenu) ContextMenuVisible() bool {
c.l.Lock()
defer c.l.Unlock()
return c.open
}
func (c *ContextMenu) show(item int, x int, y int, setFocus func(Primitive)) {
c.initializeList()
if len(c.list.items) == 0 {
return
}
c.open = true
c.item = item
c.x, c.y = x, y
c.list.Lock()
for i, item := range c.list.items {
if item.enabled {
c.list.currentItem = i
break
}
}
c.list.Unlock()
c.list.SetSelectedFunc(func(index int, item *ListItem) {
c.l.Lock()
// A context item was selected. Close the menu.
c.hide(setFocus)
if c.selected != nil {
c.l.Unlock()
c.selected(index, item.mainText, item.shortcut)
} else {
c.l.Unlock()
}
}).SetDoneFunc(func() {
c.hide(setFocus)
})
setFocus(c.list)
}
func (c *ContextMenu) hide(setFocus func(Primitive)) {
c.initializeList()
c.open = false
if c.list.HasFocus() {
setFocus(c.parent)
}
}