« 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--todo.txt12
-rw-r--r--ui.js80
2 files changed, 84 insertions, 8 deletions
diff --git a/todo.txt b/todo.txt
index dcd847f..530af15 100644
--- a/todo.txt
+++ b/todo.txt
@@ -145,3 +145,15 @@ TODO: The "Up (to <group>)" and "(This group has no items)" elements are not
       quite buttons nor grouplike items. They should be more consistent with
       actual grouplike items (i.e. same behavior and appearance), and should
       be less buggy. (Done!)
+
+TODO: "Distribute" options in play/queue menu -- "don't distribute", "spread
+      (across queue?) evenly", "spread randomly". (Done!)
+
+TODO: Investigate how to make "distribute" options work better when the
+      currently-playing song is contained in the group to be distributed.
+
+TODO: The "distribute" options shouldn't show up when you've only got a track
+      selected! But imagine you already had a "distribute" option selected when
+      you open up the menu on a track. Is there a way to not mutate the index
+      of the whereControl in that case (until the user changes the option)?
+      Like the DS home screen.
diff --git a/ui.js b/ui.js
index 6f8b07a..3496d97 100644
--- a/ui.js
+++ b/ui.js
@@ -118,14 +118,18 @@ class AppElement extends FocusElement {
         }
       }
 
-      let afterItem = null
-      if (where === 'next') {
-        afterItem = this.playingTrack
-      }
+      if (where === 'next' || where === 'end') {
+        let afterItem = null
+        if (where === 'next') {
+          afterItem = this.playingTrack
+        }
 
-      this.queueGrouplikeItem(item, afterItem, {
-        movePlayingTrack: order === 'normal'
-      })
+        this.queueGrouplikeItem(item, afterItem, {
+          movePlayingTrack: order === 'normal'
+        })
+      } else if (where.startsWith('distribute-')) {
+        this.distributeQueueGrouplikeItem(item, where.slice('distribute-'.length))
+      }
 
       if (play) {
         this.playGrouplikeItem(item)
@@ -505,6 +509,64 @@ class AppElement extends FocusElement {
     return newTrack
   }
 
+  distributeQueueGrouplikeItem(grouplike, how = 'evenly') {
+    const queue = this.queueGrouplike
+    const newItems = flattenGrouplike(grouplike).items
+
+    // Expressly do an initial pass and unqueue the items we want to queue -
+    // otherwise they would mess with the math we do afterwords.
+    for (const item of newItems) {
+      if (queue.items.includes(item)) {
+        /*
+        if (!movePlayingTrack && item === this.playingTrack) {
+          // NB: if uncommenting this code, splice item from newItems and do
+          // continue instead of return!
+          return
+        }
+        */
+        queue.items.splice(queue.items.indexOf(item), 1)
+      }
+    }
+
+    const distributeStart = queue.items.indexOf(this.playingTrack) + 1
+    const distributeEnd = queue.items.length
+    const distributeSize = distributeEnd - distributeStart
+
+    const queueItem = (item, insertIndex) => {
+      if (queue.items.includes(item)) {
+        /*
+        if (!movePlayingTrack && item === this.playingTrack) {
+          return
+        }
+        */
+        queue.items.splice(queue.items.indexOf(item), 1)
+      } else {
+        offset++
+      }
+      queue.items.splice(insertIndex, 0, item)
+    }
+
+    if (how === 'evenly') {
+      let offset = 0
+      for (const item of newItems) {
+        const insertIndex = distributeStart + Math.floor(offset)
+        queue.items.splice(insertIndex, 0, item)
+        offset++
+        offset += distributeSize / newItems.length
+      }
+    } else if (how === 'randomly') {
+      const indexes = newItems.map(() => Math.floor(Math.random() * distributeSize))
+      indexes.sort()
+      for (let i = 0; i < newItems.length; i++) {
+        const item = newItems[i]
+        const insertIndex = distributeStart + indexes[i] + i
+        queue.items.splice(insertIndex, 0, item)
+      }
+    }
+
+    this.queueListingElement.buildItems()
+  }
+
   unqueueGrouplikeItem(topItem) {
     // This function has support to unqueue groups - it removes all tracks in
     // the group recursively. (You can never unqueue a group itself from the
@@ -1177,7 +1239,9 @@ class InteractiveGrouplikeItemElement extends BasicGrouplikeItemElement {
 
     this.whereControl = new InlineListPickerElement('Where?', [
       {value: 'next', label: 'After current song'},
-      {value: 'end', label: 'At end of queue'}
+      {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?', [