Terminal-based user interface toolkit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

178 lines
3.6 KiB

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.RWMutex
}
// 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()
c.list.ShowSecondaryText(false)
c.list.SetHover(true)
c.list.SetWrapAround(true)
c.list.ShowFocus(false)
c.list.SetBorder(true)
c.list.SetPadding(
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)) {
c.l.Lock()
defer c.l.Unlock()
c.initializeList()
item := NewListItem(text)
item.SetShortcut(shortcut)
item.SetSelectedFunc(c.wrap(selected))
c.list.AddItem(item)
if text == "" && shortcut == 0 {
c.list.Lock()
index := len(c.list.items) - 1
c.list.items[index].disabled = true
c.list.Unlock()
}
}
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() {
c.l.Lock()
defer c.l.Unlock()
c.initializeList()
c.list.Clear()
}
// 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)) {
c.l.Lock()
defer c.l.Unlock()
c.selected = handler
}
// 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.disabled {
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, string(item.mainText), item.shortcut)
} else {
c.l.Unlock()
}
})
c.list.SetDoneFunc(func() {
c.l.Lock()
defer c.l.Unlock()
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)
}
}