« 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--README.md4
m---------tui-lib0
-rw-r--r--ui.js43
3 files changed, 41 insertions, 6 deletions
diff --git a/README.md b/README.md
index 55f5e82..ae072ac 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,6 @@ playlist.json file (usually generated by http-music or downloaded from online).
 * <kbd>Tab</kbd> and <kbd><kbd>Shift</kbd>+<kbd>Tab</kbd></kbd> - switch between UI elements
 * <kbd>1</kbd> - focus the main track/group listing
 * <kbd>2</kbd> - focus the queue listing
-* <kbd>t</kbd> and <kbd>T</kbd> (shift+T) - switch between playlist tabs
 * <kbd>Enter</kbd> - play the selected track
 * <kbd><kbd>Shift</kbd>+<kbd>Up</kbd></kbd> or <kbd>p</kbd> - play previous track
 * <kbd><kbd>Shift</kbd>+<kbd>Down</kbd></kbd> or <kbd>n</kbd> - play next track
@@ -32,6 +31,9 @@ playlist.json file (usually generated by http-music or downloaded from online).
 * <kbd>Right</kbd> - seek ahead
 * <kbd>Left</kbd> - seek back
 * <kbd><kbd>Ctrl</kbd>+<kbd>O</kbd></kbd> - open a playlist from a source (like a /path/to/a/folder or a YouTube playlist URL) (you can also just pass this source to `mtui`)
+* <kbd>t</kbd> and <kbd>T</kbd> (shift+T) - switch between playlist tabs
+* <kbd><kbd>Ctrl</kbd>+<kbd>T</kbd></kbd> - open the current playlist in a new tab (so, clone the current tab)
+* <kbd><kbd>Ctrl</kbd>+<kbd>W</kbd></kbd> - close the current tab
 * **In the main listing:**
   * <kbd>Enter</kbd> - if the selected item is a group, enter it; otherwise play it
   * <kbd>Backspace</kbd> - leave the current group (if in one)
diff --git a/tui-lib b/tui-lib
-Subproject de1c92141d2d4859cf869ec90e50e8eb0e5e856
+Subproject 40431bcfd46c457a1cf271b5eae53a35aa1d0b6
diff --git a/ui.js b/ui.js
index 3e04f85..4228650 100644
--- a/ui.js
+++ b/ui.js
@@ -148,7 +148,7 @@ class AppElement extends FocusElement {
 
     grouplike = await processSmartPlaylist(grouplike)
 
-    if (newTab) {
+    if (newTab || !this.tabber.currentElement) {
       const grouplikeListing = this.newGrouplikeListing()
       grouplikeListing.loadGrouplike(grouplike)
     } else {
@@ -242,6 +242,10 @@ class AppElement extends FocusElement {
       this.form.updateSelectedElement()
     } else if (keyBuf.equals(Buffer.from([15]))) { // ctrl-O
       this.openPlaylistDialog.open()
+    } else if (keyBuf.equals(Buffer.from([20]))) { // ctrl-T
+      this.cloneCurrentTab()
+    } else if (keyBuf.equals(Buffer.from([23]))) { // ctrl-W
+      this.tabber.closeTab(this.tabber.currentElement)
     } else if (this.tabber.isSelected && keyBuf.equals(Buffer.from(['t'.charCodeAt(0)]))) {
       this.tabber.nextTab()
     } else if (this.tabber.isSelected && keyBuf.equals(Buffer.from(['T'.charCodeAt(0)]))) {
@@ -251,6 +255,12 @@ class AppElement extends FocusElement {
     }
   }
 
+  cloneCurrentTab() {
+    const grouplike = this.tabber.currentElement.grouplike
+    const listing = this.newGrouplikeListing()
+    listing.loadGrouplike(grouplike)
+  }
+
   shuffleQueue() {
     const queue = this.queueGrouplike
     const index = queue.items.indexOf(this.playingTrack) + 1 // This is 0 if no track is playing
@@ -1092,8 +1102,8 @@ class Tabber extends FocusElement {
   }
 
   addTab(element) {
-    this.tabberElements.push(element)
-    this.addChild(element)
+    this.tabberElements.splice(this.currentElementIndex + 1, 0, element)
+    this.addChild(element, this.currentElementIndex + 1)
     this.listElement.buildItems()
   }
 
@@ -1116,6 +1126,25 @@ class Tabber extends FocusElement {
     this.updateVisibleElement()
   }
 
+  closeTab(element) {
+    if (!this.tabberElements.includes(element)) {
+      return
+    }
+
+    const index = this.tabberElements.indexOf(element)
+    this.tabberElements.splice(index, 1)
+    if (index <= this.currentElementIndex) {
+      this.currentElementIndex--
+    }
+
+    // Deliberately update the visible element before removing the child. If we
+    // remove the child first, the isSelected in updateVisibleElement will be
+    // false, so the new currentElement won't actually be root.select()'ed.
+    this.updateVisibleElement()
+    this.removeChild(element)
+    this.listElement.buildItems()
+  }
+
   updateVisibleElement() {
     const len = this.tabberElements.length - 1
     this.currentElementIndex = Math.min(len, Math.max(0, this.currentElementIndex))
@@ -1124,8 +1153,12 @@ class Tabber extends FocusElement {
       el.visible = (i === this.currentElementIndex)
     })
 
-    if (this.isSelected && this.currentElement) {
-      this.root.select(this.currentElement)
+    if (this.isSelected) {
+      if (this.currentElement) {
+        this.root.select(this.currentElement)
+      } else {
+        this.root.select(this)
+      }
     }
 
     this.fixLayout()