« get me outta code hell

Tabber display - 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-07-05 09:27:14 -0300
committerFlorrie <towerofnix@gmail.com>2018-07-05 09:27:14 -0300
commit6e4e3fb76cbceb34338c62fb08394ffdffcdc687 (patch)
tree96389b355c708819306722d963095dda2b4ca2a5
parentf4c4a51c99c7e00d2cebc705e9c2b3b13894c046 (diff)
Tabber display
-rw-r--r--todo.txt9
m---------tui-lib0
-rw-r--r--ui.js119
3 files changed, 125 insertions, 3 deletions
diff --git a/todo.txt b/todo.txt
index 8707d4b..62b9ec4 100644
--- a/todo.txt
+++ b/todo.txt
@@ -69,6 +69,7 @@ TODO: Make PathElements selectable (again).
 TODO: Make the tabber UI show handy information, like what tab you've got
       selected, how many tabs there are, the usual (same way a browser tabber
       works).
+      (Done!)
 
 TODO: A "bookmarked playlists" list in the UI, so you can quickly load up
       playlists you often use. (Let anything go here, like YT playlist URLs,
@@ -77,3 +78,11 @@ TODO: A "bookmarked playlists" list in the UI, so you can quickly load up
 TODO: Get rid of "WARNING: unable to extract uploader nickname", which comes
       from youtube-dl.
       (Done!)
+
+TODO: Fix crashes related to the window resizing while a download is occurring
+      (i.e. the braille "download spinner" is showing). Very weird!
+
+TODO: Investigate performance issues with very very long ListScrollForms.
+
+TODO: At some point, putting mtui downloads in ~/.mtui - but preferrably with
+      a more humanish structure - would be quite nice..!
diff --git a/tui-lib b/tui-lib
-Subproject eebff4fbbac489f96954f05d6d0d838c62a8e6c
+Subproject de1c92141d2d4859cf869ec90e50e8eb0e5e856
diff --git a/ui.js b/ui.js
index ab85bf7..3e04f85 100644
--- a/ui.js
+++ b/ui.js
@@ -674,6 +674,14 @@ class GrouplikeListingElement extends FocusElement {
   selectAndShow(item) {
     this.form.selectAndShow(item)
   }
+
+  get tabberLabel() {
+    if (this.grouplike) {
+      return this.grouplike.name || 'Unnamed group'
+    } else {
+      return 'No group open'
+    }
+  }
 }
 
 class GrouplikeListingForm extends ListScrollForm {
@@ -1050,18 +1058,43 @@ class Tabber extends FocusElement {
 
     this.tabberElements = []
     this.currentElementIndex = 0
+
+    this.listElement = new TabberList(this)
+    this.addChild(this.listElement)
   }
 
   fixLayout() {
-    if (this.currentElement) {
-      this.currentElement.fillParent()
-      this.currentElement.fixLayout()
+    const el = this.currentElement
+    if (el) {
+      // Only make space for the tab list if there's more than one tab visible.
+      // (The tab list isn't shown if there's only one.)
+      if (this.tabberElements.length > 1) {
+        el.w = this.contentW
+        el.h = this.contentH - 1
+        el.x = 0
+        el.y = 1
+      } else {
+        el.fillParent()
+        el.x = 0
+        el.y = 0
+      }
+      el.fixLayout()
+    }
+
+    if (this.tabberElements.length > 1) {
+      this.listElement.visible = true
+      this.listElement.w = this.contentW
+      this.listElement.h = 1
+      this.listElement.fixLayout()
+    } else {
+      this.listElement.visible = false
     }
   }
 
   addTab(element) {
     this.tabberElements.push(element)
     this.addChild(element)
+    this.listElement.buildItems()
   }
 
   nextTab() {
@@ -1113,4 +1146,84 @@ class Tabber extends FocusElement {
   }
 }
 
+class TabberList extends ListScrollForm {
+  constructor(tabber) {
+    super('horizontal')
+    this.tabber = tabber
+    this.captureTab = false
+  }
+
+  buildItems() {
+    while (this.inputs.length) {
+      this.removeInput(this.inputs[0])
+    }
+
+    for (const item of this.tabber.tabberElements) {
+      const element = new TabberListItem(item, this.tabber)
+      this.addInput(element)
+      element.fixLayout()
+    }
+
+    this.scrollToEnd()
+    this.fixLayout()
+  }
+
+  fixLayout() {
+    this.w = this.parent.contentW
+    this.h = 1
+    this.x = 0
+    this.y = 0
+    this.scrollElementIntoEndOfView(this.inputs[this.curIndex])
+    super.fixLayout()
+  }
+
+  drawTo() {
+    let changed = false
+    for (const input of this.inputs) {
+      input.fixLayout()
+      if (input._oldW !== input.w) {
+        input._oldW = input.w
+        changed = true
+      }
+    }
+    if (changed) {
+      this.fixLayout()
+    }
+  }
+
+  // TODO: Be less hacky about these! Right now the tabber list is totally not
+  // interactive.
+  get curIndex() { return this.tabber.currentElementIndex }
+  set curIndex(newVal) {}
+}
+
+class TabberListItem extends FocusElement {
+  constructor(tab, tabber) {
+    super()
+
+    this.tab = tab
+    this.tabber = tabber
+  }
+
+  fixLayout() {
+    this.w = this.text.length + 3
+  }
+
+  drawTo(writable) {
+    if (this.tabber.currentElement === this.tab) {
+      writable.write(ansi.setAttributes([ansi.A_BRIGHT]))
+      writable.write(ansi.moveCursor(this.absTop, this.absLeft))
+      writable.write('<' + this.text + '>')
+      writable.write(ansi.resetAttributes())
+    } else {
+      writable.write(ansi.moveCursor(this.absTop, this.absLeft + 1))
+      writable.write(this.text)
+    }
+  }
+
+  get text() {
+    return this.tab.tabberLabel || 'a(n) ' + this.tab.constructor.name
+  }
+}
+
 module.exports.AppElement = AppElement