« 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.js151
1 files changed, 146 insertions, 5 deletions
diff --git a/ui.js b/ui.js
index a167dab..8d89722 100644
--- a/ui.js
+++ b/ui.js
@@ -32,6 +32,11 @@ const {
 } = require('./playlist-utils')
 
 const {
+  updateRestoredTracksUsingPlaylists,
+  getWaitingTrackData
+} = require('./serialized-backend')
+
+const {
   ui: {
     Dialog,
     DisplayElement,
@@ -54,7 +59,7 @@ const {
 } = require('tui-lib')
 
 /* text editor features disabled because theyre very much incomplete and havent
- * gotten much use from me or anyonea afaik!
+ * gotten much use from me or anyone afaik!
 const TuiTextEditor = require('tui-text-editor')
 */
 
@@ -191,6 +196,8 @@ class AppElement extends FocusElement {
     this.isPartyHost = false
     this.enableAutoDJ = false
 
+    this.playlistSources = []
+
     this.config = Object.assign({
       canControlPlayback: true,
       canControlQueue: true,
@@ -238,6 +245,18 @@ class AppElement extends FocusElement {
     })
     */
 
+    this.logPane = new Pane()
+    this.addChild(this.logPane)
+
+    this.log = new Log()
+    this.logPane.addChild(this.log)
+    this.logPane.visible = false
+
+    this.log.on('log-message', () => {
+      this.logPane.visible = true
+      this.fixLayout()
+    })
+
     if (!this.config.showTabberPane) {
       this.tabberPane.visible = false
     }
@@ -430,12 +449,13 @@ class AppElement extends FocusElement {
 
   bindListeners() {
     for (const key of [
-      'handlePlaying',
+      'handlePlayingDetails',
       'handleReceivedTimeData',
       'handleProcessMetadataProgress',
       'handleQueueUpdated',
       'handleAddedQueuePlayer',
       'handleRemovedQueuePlayer',
+      'handleLogMessage',
       'handleSetLoopQueueAtEnd'
     ]) {
       this[key] = this[key].bind(this)
@@ -469,7 +489,7 @@ class AppElement extends FocusElement {
     PIE.on('toggle pause', () => PIE.queuePlayer.togglePause())
 
     queuePlayer.on('received time data', this.handleReceivedTimeData)
-    queuePlayer.on('playing', this.handlePlaying)
+    queuePlayer.on('playing details', this.handlePlayingDetails)
     queuePlayer.on('queue updated', this.handleQueueUpdated)
   }
 
@@ -498,7 +518,7 @@ class AppElement extends FocusElement {
     }
 
     queuePlayer.removeListener('receivedTimeData', this.handleReceivedTimeData)
-    queuePlayer.removeListener('playing', this.handlePlaying)
+    queuePlayer.removeListener('playing details', this.handlePlayingDetails)
     queuePlayer.removeListener('queue updated', this.handleQueueUpdated)
     queuePlayer.stopPlaying()
   }
@@ -507,6 +527,7 @@ class AppElement extends FocusElement {
     this.backend.on('processMetadata progress', this.handleProcessMetadataProgress)
     this.backend.on('added queue player', this.handleAddedQueuePlayer)
     this.backend.on('removed queue player', this.handleRemovedQueuePlayer)
+    this.backend.on('log message', this.handleLogMessage)
     this.backend.on('set-loop-queue-at-end', this.handleSetLoopQueueAtEnd)
   }
 
@@ -514,6 +535,7 @@ class AppElement extends FocusElement {
     this.backend.removeListener('processMetadata progress', this.handleProcessMetadataProgress)
     this.backend.removeListener('added queue player', this.handleAddedQueuePlayer)
     this.backend.removeListener('removed queue player', this.handleRemovedQueuePlayer)
+    this.backend.removeListener('log message', this.handleLogMessage)
     this.backend.removeListener('set-loop-queue-at-end', this.handleSetLoopQueueAtEnd)
   }
 
@@ -528,11 +550,15 @@ class AppElement extends FocusElement {
     }
   }
 
+  handleLogMessage(messageInfo) {
+    this.log.newLogMessage(messageInfo)
+  }
+
   handleSetLoopQueueAtEnd() {
     this.updateQueueLengthLabel()
   }
 
-  async handlePlaying(track, oldTrack, queuePlayer) {
+  async handlePlayingDetails(track, oldTrack, queuePlayer) {
     const PIE = this.getPlaybackInfoElementForQueuePlayer(queuePlayer)
     if (PIE) {
       PIE.updateTrack()
@@ -1216,6 +1242,9 @@ class AppElement extends FocusElement {
 
     grouplike = await processSmartPlaylist(grouplike)
 
+    this.playlistSources.push(grouplike)
+    updateRestoredTracksUsingPlaylists(this.backend, this.playlistSources)
+
     if (!this.tabber.currentElement || newTab && this.tabber.currentElement.grouplike) {
       const grouplikeListing = this.newGrouplikeListing()
       grouplikeListing.loadGrouplike(grouplike)
@@ -1329,10 +1358,21 @@ class AppElement extends FocusElement {
     }
     */
 
+    if (this.logPane.visible) {
+      this.logPane.w = leftWidth
+      this.logPane.h = 6
+      this.log.fillParent()
+      this.log.fixAllLayout()
+    }
+
     if (this.tabberPane.visible) {
       this.tabberPane.w = leftWidth
       this.tabberPane.y = bottomY
       this.tabberPane.h = topY - this.tabberPane.y
+      if (this.logPane.visible) {
+        this.tabberPane.h -= this.logPane.h
+        this.logPane.y = this.tabberPane.bottom
+      }
       /*
       if (this.textInfoPane.visible) {
         this.tabberPane.h -= this.textInfoPane.h
@@ -3531,6 +3571,7 @@ class PlaybackInfoElement extends FocusElement {
 
   refreshTrackText(maxNameWidth = Infinity) {
     const { playingTrack } = this.queuePlayer
+    const waitingTrackData = getWaitingTrackData(this.queuePlayer)
     if (playingTrack) {
       this.currentTrack = playingTrack
       const { name } = playingTrack
@@ -3542,6 +3583,11 @@ class PlaybackInfoElement extends FocusElement {
       this.progressBarLabel.text = ''
       this.progressTextLabel.text = '(Starting..)'
       this.timeData = {}
+    } else if (waitingTrackData) {
+      const { name } = waitingTrackData
+      this.clearInfoText()
+      this.trackNameLabel.text = name
+      this.progressTextLabel.text = '(Waiting to play, once found in playlist source.)'
     } else {
       this.clearInfoText()
     }
@@ -4554,4 +4600,99 @@ class NotesTextEditor extends TuiTextEditor {
 }
 */
 
+class Log extends ListScrollForm {
+  constructor() {
+    super('vertical')
+  }
+
+  newLogMessage(messageInfo) {
+    if (this.inputs.length === 10) {
+      this.removeInput(this.inputs[0])
+    }
+
+    if (messageInfo.mayCombine) {
+      // If a message is specified to "combine", it'll replace an immediately
+      // previous message of the same code and sender.
+      const previous = this.inputs[this.inputs.length - 1]
+      if (
+        previous &&
+        previous.info.code === messageInfo.code &&
+        previous.info.sender === messageInfo.sender
+      ) {
+        // If the code and sender match, just remove the previous message.
+        // It'll be replaced by the one we're about to add!
+        this.removeInput(previous)
+      }
+    }
+
+    const logMessage = new LogMessage(messageInfo)
+    this.addInput(logMessage)
+    this.fixLayout()
+    this.scrollToEnd()
+    this.emit('log-message', logMessage)
+    return logMessage
+  }
+}
+
+class LogMessage extends FocusElement {
+  constructor(info) {
+    super()
+
+    this.info = info
+
+    const {
+      text,
+      isVerbose = false
+    } = info
+
+    this.label = new LogMessageLabel(text, isVerbose)
+    this.addChild(this.label)
+  }
+
+  fixLayout() {
+    this.w = this.parent.contentW
+    this.label.w = this.contentW
+    this.h = this.label.h
+  }
+
+  clicked(button) {
+    if (button === 'left') {
+      this.root.select(this)
+      return false
+    }
+  }
+}
+
+class LogMessageLabel extends WrapLabel {
+  constructor(text, isVerbose = false) {
+    super(text)
+
+    this.isVerbose = isVerbose
+  }
+
+  writeTextTo(writable) {
+    const w = this.w
+    const lines = this.getWrappedLines()
+    for (let i = 0; i < lines.length; i++) {
+      const text = this.processFormatting(lines[i])
+      writable.write(ansi.moveCursor(this.absTop + i, this.absLeft))
+      writable.write(text)
+      const width = ansi.measureColumns(text)
+      if (width < w && this.textAttributes.length) {
+        writable.write(ansi.setAttributes([ansi.A_RESET, ...this.textAttributes]))
+        writable.write(' '.repeat(w - width))
+      }
+    }
+  }
+
+  set textAttributes(val) {}
+
+  get textAttributes() {
+    return [
+      this.parent.isSelected ? 40 : null,
+      this.isVerbose ? 2 : null
+    ].filter(x => x !== null)
+  }
+}
+
 module.exports = AppElement