« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/ui.js
diff options
context:
space:
mode:
Diffstat (limited to 'ui.js')
-rw-r--r--ui.js190
1 files changed, 95 insertions, 95 deletions
diff --git a/ui.js b/ui.js
index 3496d97..038c008 100644
--- a/ui.js
+++ b/ui.js
@@ -94,6 +94,19 @@ class AppElement extends FocusElement {
     */
     this.menu = new ContextMenu()
     this.addChild(this.menu)
+
+    this.whereControl = new InlineListPickerElement('Where?', [
+      {value: 'next', label: 'After current song'},
+      {value: 'end', label: 'At end of queue'},
+      {value: 'distribute-evenly', label: 'Distributed across queue evenly'},
+      {value: 'distribute-randomly', label: 'Distributed across queue randomly'}
+    ])
+
+    this.orderControl = new InlineListPickerElement('Order?', [
+      {value: 'shuffle', label: 'Shuffle all'},
+      {value: 'shuffle-groups', label: 'Shuffle order of groups'},
+      {value: 'normal', label: 'In order'}
+    ])
   }
 
   selected() {
@@ -107,36 +120,8 @@ class AppElement extends FocusElement {
 
     grouplikeListing.on('download', item => this.downloadGrouplikeItem(item))
     grouplikeListing.on('browse', item => grouplikeListing.loadGrouplike(item))
-    grouplikeListing.on('menu', (item, opts) => this.menu.show(opts))
-
-    grouplikeListing.on('queue', (item, {where = 'end', order = 'normal', play = false} = {}) => {
-      if (isGroup(item)) {
-        if (order === 'shuffle') {
-          item = {items: shuffleArray(flattenGrouplike(item).items)}
-        } else if (order === 'shuffle-groups') {
-          item = shuffleOrderOfGroups(item)
-        }
-      }
-
-      if (where === 'next' || where === 'end') {
-        let afterItem = null
-        if (where === 'next') {
-          afterItem = this.playingTrack
-        }
-
-        this.queueGrouplikeItem(item, afterItem, {
-          movePlayingTrack: order === 'normal'
-        })
-      } else if (where.startsWith('distribute-')) {
-        this.distributeQueueGrouplikeItem(item, where.slice('distribute-'.length))
-      }
-
-      if (play) {
-        this.playGrouplikeItem(item)
-      }
-    })
-
-    grouplikeListing.on('unqueue', item => this.unqueueGrouplikeItem(item))
+    grouplikeListing.on('menu', (item, el) => this.showMenuForItemElement(el))
+    grouplikeListing.on('queue', (item, opts) => this.handleQueueOptions(item, opts))
 
     const updateListingsFor = item => {
       for (const grouplikeListing of this.tabber.tabberElements) {
@@ -241,7 +226,54 @@ class AppElement extends FocusElement {
     }
 
     grouplikeListing.pathElement.on('select', item => handleSelectFromPathElement(item))
+  }
+
+  showMenuForItemElement(el) {
+    const emitControls = play => () => {
+      this.handleQueueOptions(item, {
+        where: this.whereControl.curValue,
+        order: this.orderControl.curValue,
+        play: play
+      })
+    }
+
+    const { item, isGroup, isMarked } = el
+    const { editMode } = this
+    const anyMarked = editMode && this.markGrouplike.items.length > 0
+    this.menu.show({
+      x: el.absLeft,
+      y: el.absTop + 1,
+      items: [
+        // A label that just shows some brief information about the item.
+        isGroup && {label:
+          `("${item.name}" -` +
+          ` ${item.items.length} item${item.items.length === 1 ? '' : 's'}` +
+          `, ${countTotalItems(item)} total` +
+          ')'},
+
+        // The actual controls!
+        {divider: true},
 
+        // TODO: Don't emit these on the element (and hence receive them from
+        // the listing) - instead, handle their behavior directly. We'll want
+        // to move the "mark"/"paste" (etc) code into separate functions,
+        // instead of just defining their behavior inside the listing event
+        // handlers.
+        editMode && {label: isMarked ? 'Unmark' : 'Mark', action: () => el.emit('mark')},
+        anyMarked && {label: 'Paste (above)', action: () => el.emit('paste', {where: 'above'})},
+        anyMarked && {label: 'Paste (below)', action: () => el.emit('paste', {where: 'below'})},
+        // anyMarked && !this.isReal && {label: 'Paste', action: () => this.emit('paste')}, // No "above" or "elow" in the label because the "fake" item/row will be replaced (it'll disappear, since there'll be an item in the group)
+        editMode && {divider: true},
+
+        {element: this.whereControl},
+        {element: this.orderControl},
+        {label: 'Play!', action: emitControls(true)},
+        {label: 'Queue!', action: emitControls(false)},
+        {divider: true},
+
+        {label: 'Remove from queue', action: () => this.unqueueGrouplikeItem(item)}
+      ]
+    })
   }
 
   async handlePlaylistSource(source, newTab = false) {
@@ -450,7 +482,37 @@ class AppElement extends FocusElement {
     this.player.kill()
   }
 
-  async queueGrouplikeItem(topItem, afterItem = null, {movePlayingTrack = true} = {}) {
+  // TODO: I'd like to name/incorporate this function better.. for now it's
+  // just directly moved from the old event listener on grouplikeListings for
+  // 'queue'.
+  handleQueueOptions(item, {where = 'end', order = 'normal', play = false} = {}) {
+    if (isGroup(item)) {
+      if (order === 'shuffle') {
+        item = {items: shuffleArray(flattenGrouplike(item).items)}
+      } else if (order === 'shuffle-groups') {
+        item = shuffleOrderOfGroups(item)
+      }
+    }
+
+    if (where === 'next' || where === 'end') {
+      let afterItem = null
+      if (where === 'next') {
+        afterItem = this.playingTrack
+      }
+
+      this.queueGrouplikeItem(item, afterItem, {
+        movePlayingTrack: order === 'normal'
+      })
+    } else if (where.startsWith('distribute-')) {
+      this.distributeQueueGrouplikeItem(item, where.slice('distribute-'.length))
+    }
+
+    if (play) {
+      this.playGrouplikeItem(item)
+    }
+  }
+
+  queueGrouplikeItem(topItem, afterItem = null, {movePlayingTrack = true} = {}) {
     const newTrackIndex = this.queueGrouplike.items.length
 
     // The position which new tracks should be added at, if afterItem is
@@ -1236,19 +1298,6 @@ class InteractiveGrouplikeItemElement extends BasicGrouplikeItemElement {
     super(item.name)
     this.item = item
     this.recordStore = recordStore
-
-    this.whereControl = new InlineListPickerElement('Where?', [
-      {value: 'next', label: 'After current song'},
-      {value: 'end', label: 'At end of queue'},
-      {value: 'distribute-evenly', label: 'Distributed across queue evenly'},
-      {value: 'distribute-randomly', label: 'Distributed across queue randomly'}
-    ])
-
-    this.orderControl = new InlineListPickerElement('Order?', [
-      {value: 'shuffle', label: 'Shuffle all'},
-      {value: 'shuffle-groups', label: 'Shuffle order of groups'},
-      {value: 'normal', label: 'In order'}
-    ])
   }
 
   drawTo(writable) {
@@ -1272,7 +1321,7 @@ class InteractiveGrouplikeItemElement extends BasicGrouplikeItemElement {
     } else if (telc.isCaselessLetter(keyBuf, 'x')) {
       this.emit('remove')
     } else if (telc.isCaselessLetter(keyBuf, 'm') || telc.isCaselessLetter(keyBuf, 'f')) {
-      this.showMenu()
+      this.emit('menu', this)
     }
   }
 
@@ -1290,60 +1339,11 @@ class InteractiveGrouplikeItemElement extends BasicGrouplikeItemElement {
       return false
     } else if (button === 'right') {
       this.parent.selectInput(this)
-      this.showMenu()
+      this.emit('menu', this)
       return false
     }
   }
 
-  showMenu() {
-    const editMode = this.recordStore.app.editMode
-    const anyMarked = editMode && !!this.recordStore.app.markGrouplike.items.length
-    this.emit('menu', {
-      x: this.absLeft,
-      y: this.absTop + 1,
-      items: [
-        // A label that just shows some brief information about the item.
-        this.isGroup && {label:
-          `("${this.item.name}" -` +
-          ` ${this.item.items.length} item${this.item.items.length === 1 ? '' : 's'}` +
-          `, ${countTotalItems(this.item)} total` +
-          ')'},
-
-        // The actual controls!
-        {divider: true},
-        editMode && {label: this.isMarked ? 'Unmark' : 'Mark', action: () => this.emit('mark')},
-        anyMarked && {label: 'Paste (above)', action: () => this.emit('paste', {where: 'above'})},
-        anyMarked && {label: 'Paste (below)', action: () => this.emit('paste', {where: 'below'})},
-        // anyMarked && !this.isReal && {label: 'Paste', action: () => this.emit('paste')}, // No "above" or "elow" in the label because the "fake" item/row will be replaced (it'll disappear, since there'll be an item in the group)
-        editMode && {divider: true},
-
-        {element: this.whereControl},
-        {element: this.orderControl},
-        {label: 'Play!', action: () => this.playWithControls()},
-        {label: 'Queue!', action: () => this.queueWithControls()},
-        {divider: true},
-
-        {label: 'Remove from queue', action: () => this.emit('unqueue')}
-      ]
-    })
-  }
-
-  playWithControls() {
-    this.emitControls(true)
-  }
-
-  queueWithControls() {
-    this.emitControls(false)
-  }
-
-  emitControls(play) {
-    this.emit('queue', {
-      where: this.whereControl.curValue,
-      order: this.orderControl.curValue,
-      play: play
-    })
-  }
-
   writeStatus(writable) {
     if (this.isGroup) {
       // The ANSI attributes here will apply to the rest of the line, too.