« 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.js84
1 files changed, 83 insertions, 1 deletions
diff --git a/ui.js b/ui.js
index 3da7f5c..1d8d2da 100644
--- a/ui.js
+++ b/ui.js
@@ -708,6 +708,16 @@ class GrouplikeListingElement extends Form {
       }
     })
 
+    this.jumpElement = new ListingJumpElement()
+    this.addInput(this.jumpElement)
+    this.jumpElement.visible = false
+
+    this.jumpElement.on('cancel', () => {
+      this.hideJumpElement()
+    })
+
+    this.jumpElement.on('value', value => this.handleJumpValue(value))
+
     this.pathElement = new PathElement()
     this.addInput(this.pathElement)
 
@@ -727,9 +737,15 @@ class GrouplikeListingElement extends Form {
     this.form.y = this.commentLabel.bottom
     this.form.h -= this.commentLabel.h
     this.form.h -= 1 // For the path element
+    if (this.jumpElement.visible) this.form.h -= 1
+
+    this.form.fixLayout() // Respond to being resized
 
     this.pathElement.y = this.contentH - 1
     this.pathElement.w = this.contentW
+
+    this.jumpElement.y = this.pathElement.y - 1
+    this.jumpElement.w = this.contentW
   }
 
   selected() {
@@ -744,6 +760,8 @@ class GrouplikeListingElement extends Form {
   keyPressed(keyBuf) {
     if (telc.isBackspace(keyBuf)) {
       this.loadParentGrouplike()
+    } else if (telc.isCharacter(keyBuf, '/') || keyBuf[0] === 6) { // '/', ctrl-F
+      this.showJumpElement()
     } else {
       return super.keyPressed(keyBuf)
     }
@@ -837,6 +855,40 @@ class GrouplikeListingElement extends Form {
     this.form.selectAndShow(item)
   }
 
+  handleJumpValue(value) {
+    // Don't perform the search if the user didn't enter anything.
+    if (value.length) {
+      const lower = value.toLowerCase()
+      const getName = inp => (inp.item && inp.item.name) ? inp.item.name.toLowerCase().trim() : ''
+      // TODO: Search past the current index, for repeated searches?
+      const startsIndex = this.form.inputs.findIndex(inp => getName(inp).startsWith(lower))
+      const includesIndex = this.form.inputs.findIndex(inp => getName(inp).includes(lower))
+      const matchedIndex = startsIndex >= 0 ? startsIndex : includesIndex
+
+      if (matchedIndex >= 0) {
+        this.form.curIndex = matchedIndex
+        this.form.scrollSelectedElementIntoView()
+      } else {
+        // TODO: Feedback that the search failed.. right now we just close the
+        // jump-to menu, which might not be right.
+      }
+    }
+
+    this.hideJumpElement()
+  }
+
+  showJumpElement() {
+    this.jumpElement.visible = true
+    this.root.select(this.jumpElement)
+    this.fixLayout()
+  }
+
+  hideJumpElement() {
+    this.jumpElement.visible = false
+    this.root.select(this)
+    this.fixLayout()
+  }
+
   get tabberLabel() {
     if (this.grouplike) {
       return this.grouplike.name || 'Unnamed group'
@@ -872,7 +924,6 @@ class GrouplikeListingForm extends ListScrollForm {
   }
 
   selectAndShow(item) {
-    // TODO: Make sure this is still working.
     const index = this.inputs.findIndex(inp => inp.item === item)
     if (index >= 0) {
       this.curIndex = index
@@ -998,6 +1049,37 @@ class GrouplikeItemElement extends Button {
   }
 }
 
+class ListingJumpElement extends Form {
+  constructor() {
+    super()
+
+    this.label = new Label('Jump to: ')
+    this.addChild(this.label)
+
+    this.input = new TextInput()
+    this.addInput(this.input)
+
+    this.input.on('value', value => {
+      this.emit('value', value)
+    })
+
+    this.input.on('cancel', () => {
+      this.emit('cancel')
+    })
+  }
+
+  selected() {
+    this.input.value = ''
+    this.input.keepCursorInRange()
+    this.root.select(this.input)
+  }
+
+  fixLayout() {
+    this.input.x = this.label.right
+    this.input.w = this.contentW - this.input.x
+  }
+}
+
 class PathElement extends ListScrollForm {
   constructor() {
     super('horizontal')