« get me outta code hell

Move menu behavior out of grouplike item elements - mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2018-12-21 11:26:29 -0400
committerFlorrie <towerofnix@gmail.com>2018-12-21 11:35:54 -0400
commit391a5b1329190c40aaa6c17f0281bab27eee8ce3 (patch)
tree07b3d5315bdb4bc9cfb376eb7a0d778765a4f295
parent8438f8319149dae9f594a57eb0807ba2f8bba7d8 (diff)
Move menu behavior out of grouplike item elements
This also makes it so that the selected options in whereControl and
orderControl stay the same when you open the context menu on a different
item, browse a different group, etc.
-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.