« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ui.js59
1 files changed, 49 insertions, 10 deletions
diff --git a/ui.js b/ui.js
index 5371b56..840c7ee 100644
--- a/ui.js
+++ b/ui.js
@@ -256,7 +256,7 @@ class AppElement extends FocusElement {
       {value: 'end', label: 'At end of queue'},
       {value: 'distribute-evenly', label: 'Distributed across queue evenly'},
       {value: 'distribute-randomly', label: 'Distributed across queue randomly'}
-    ])
+    ], this.showContextMenu)
 
     this.orderControl = new InlineListPickerElement('Order?', [
       {value: 'shuffle', label: 'Shuffle all'},
@@ -264,7 +264,7 @@ class AppElement extends FocusElement {
       {value: 'reverse', label: 'Reverse all'},
       {value: 'reverse-groups', label: 'Reverse order of groups'},
       {value: 'normal', label: 'In order'}
-    ])
+    ], this.showContextMenu)
 
     this.menubar.buildItems([
       {text: 'mtui', menuItems: [
@@ -494,8 +494,11 @@ class AppElement extends FocusElement {
   }
 
   showContextMenu(opts) {
-    const menu = new ContextMenu()
+    const menu = new ContextMenu(this.showContextMenu)
     this.menuLayer.addChild(menu)
+    if (opts.beforeShowing) {
+      opts.beforeShowing(menu)
+    }
     menu.show(opts)
     return menu
   }
@@ -1754,10 +1757,11 @@ class InlineListPickerElement extends FocusElement {
   // next or previous. (That's the point, it's inline.) This element is mainly
   // useful in forms or ContextMenus.
 
-  constructor(labelText, options) {
+  constructor(labelText, options, showContextMenu = null) {
     super()
     this.labelText = labelText
     this.options = options
+    this.showContextMenu = showContextMenu
     this.curIndex = 0
     this.keyboardIdentifier = this.labelText
   }
@@ -1809,6 +1813,17 @@ class InlineListPickerElement extends FocusElement {
       this.nextOption()
     } else if (telc.isLeft(keyBuf)) {
       this.previousOption()
+    } else if (input.isMenu(keyBuf) && this.showContextMenu) {
+      this.showContextMenu({
+        x: this.absLeft + ansi.measureColumns(this.labelText) + 1,
+        y: this.absTop + 1,
+        items: this.options.map(({ value, label }, index) => ({
+          label: label,
+          action: () => {
+            this.curIndex = index
+          }
+        }))
+      })
     } else {
       return true
     }
@@ -2886,7 +2901,7 @@ class TabberListItem extends FocusElement {
 }
 
 class ContextMenu extends FocusElement {
-  constructor() {
+  constructor(showContextMenu) {
     super()
 
     this.pane = new Pane()
@@ -2898,6 +2913,10 @@ class ContextMenu extends FocusElement {
     this.keyboardSelector = new KeyboardSelector(this.form)
 
     this.visible = false
+
+    this.showContextMenu = showContextMenu
+    this.showSubmenu = this.showSubmenu.bind(this)
+    this.submenu = null
   }
 
   show({x = 0, y = 0, items}) {
@@ -2906,10 +2925,9 @@ class ContextMenu extends FocusElement {
       return
     }
 
-    // This *should* work with a menu action which opens the menu again,
-    // because the selected element will be restored before the menu is
-    // opened the second time.
-    this.selectedBefore = this.root.selectedElement
+    if (!this.root.selectedElement.directAncestors.includes(this)) {
+      this.selectedBefore = this.root.selectedElement
+    }
 
     this.clearItems()
 
@@ -2932,6 +2950,7 @@ class ContextMenu extends FocusElement {
       if (item.element) {
         addDividerIfWanted()
         this.form.addInput(item.element)
+        item.element.showContextMenu = this.showSubmenu
       } else if (item.divider) {
         wantDivider = true
       } else {
@@ -2955,6 +2974,21 @@ class ContextMenu extends FocusElement {
     this.keyboardSelector.reset()
   }
 
+  showSubmenu(opts) {
+    this.showContextMenu(Object.assign({}, opts, {
+      // We need to get a reference to the submenu before it is shown, or else
+      // the parent menu will be closed (from being unselected and not knowing
+      // that a submenu was just opened).
+      beforeShowing: menu => {
+        this.submenu = menu
+      }
+    }))
+
+    this.submenu.on('close', () => {
+      this.submenu = null
+    })
+  }
+
   keyPressed(keyBuf) {
     if (telc.isEscape(keyBuf) || telc.isBackspace(keyBuf)) {
       this.close()
@@ -2966,7 +3000,12 @@ class ContextMenu extends FocusElement {
   }
 
   unselected() {
-    this.selectedBefore = this.root.selectedElement
+    // Don't close if we just opened a submenu!
+    const newEl = this.root.selectedElement
+    if (this.submenu && newEl.directAncestors.includes(this.submenu)) {
+      return
+    }
+
     if (this.visible) {
       this.close()
     }