From fbebce4a7252fcbf93da3b19c55e22366885ccf2 Mon Sep 17 00:00:00 2001 From: Florrie Date: Sun, 11 Nov 2018 14:48:15 -0400 Subject: Clean up and debuggify "fake" listing elements I.e, the "Up (to group)" button and the "(This group is empty)" pseudo-playlist-item. --- todo.txt | 10 ++++- ui.js | 151 +++++++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 99 insertions(+), 62 deletions(-) diff --git a/todo.txt b/todo.txt index 916ec66..dcd847f 100644 --- a/todo.txt +++ b/todo.txt @@ -136,4 +136,12 @@ TODO: Entering more than one key "at once" into a text input element will only TODO: Pressing space while an "Up (to )" button is selected both activates the button and pauses music (because the app detects that the space key is pressed). This is definitely wrong (it should do one or the - other - I'm not too sure which, yet, but probably the latter). + other - I'm not too sure which, yet, but probably the latter). (Done!) + +TODO: If a track's file is a symlink, the "From:" label should show where it + links to. + +TODO: The "Up (to )" 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!) diff --git a/ui.js b/ui.js index 04d5e3c..5ddba13 100644 --- a/ui.js +++ b/ui.js @@ -817,28 +817,24 @@ class GrouplikeListingElement extends Form { const parent = this.grouplike[parentSymbol] if (parent) { - const upButton = new Button('Up (to ' + (parent.name || 'unnamed group') + ')') + const upButton = new BasicGrouplikeItemElement(`Up (to ${parent.name || 'unnamed group'})`) upButton.on('pressed', () => this.loadParentGrouplike()) form.addInput(upButton) } - let itemElements = [] if (this.grouplike.items.length) { - itemElements = this.grouplike.items.map(item => new GrouplikeItemElement(item, this.recordStore)) - } else if (!this.grouplike.isTheQueue) { - const fakeItem = { - fake: true, - name: '(This group is empty)', - [parentSymbol]: this.grouplike - } - itemElements = [new GrouplikeItemElement(fakeItem, this.recordStore)] - } + const itemElements = this.grouplike.items.map(item => { + return new InteractiveGrouplikeItemElement(item, this.recordStore) + }) - for (const itemElement of itemElements) { - for (const evtName of ['download', 'remove', 'mark', 'paste', 'browse', 'queue', 'unqueue', 'menu']) { - itemElement.on(evtName, (...data) => this.emit(evtName, itemElement.item, ...data)) + for (const itemElement of itemElements) { + for (const evtName of ['download', 'remove', 'mark', 'paste', 'browse', 'queue', 'unqueue', 'menu']) { + itemElement.on(evtName, (...data) => this.emit(evtName, itemElement.item, ...data)) + } + form.addInput(itemElement) } - form.addInput(itemElement) + } else if (!this.grouplike.isTheQueue) { + form.addInput(new BasicGrouplikeItemElement('(This group is empty)')) } if (wasSelected) { @@ -956,7 +952,7 @@ class GrouplikeListingForm extends ListScrollForm { } get firstItemIndex() { - return Math.max(0, this.inputs.findIndex(el => el instanceof GrouplikeItemElement)) + return Math.max(0, this.inputs.findIndex(el => el instanceof InteractiveGrouplikeItemElement)) } selectAndShow(item) { @@ -971,12 +967,11 @@ class GrouplikeListingForm extends ListScrollForm { } } -class GrouplikeItemElement extends Button { - constructor(item, recordStore) { +class BasicGrouplikeItemElement extends Button { + constructor(text) { super() - this.item = item - this.recordStore = recordStore + this.text = text } fixLayout() { @@ -1000,60 +995,48 @@ class GrouplikeItemElement extends Button { writable.write(ansi.moveCursor(this.absTop, this.absLeft)) - if (isGroup(this.item)) { - writable.write(ansi.setAttributes([ansi.C_BLUE, ansi.A_BRIGHT])) - } - this.drawX = this.x this.writeStatus(writable) - writable.write(this.item.name.slice(0, this.w - this.drawX)) - this.drawX += this.item.name.length + writable.write(this.text.slice(0, this.w - this.drawX)) + this.drawX += this.text.length writable.write(' '.repeat(Math.max(0, this.w - this.drawX))) writable.write(ansi.resetAttributes()) } writeStatus(writable) { - this.drawX += 3 - - const braille = '⠈⠐⠠⠄⠂⠁' - const brailleChar = braille[Math.floor(Date.now() / 250) % 6] - - const record = this.recordStore.getRecord(this.item) - - if (this.isMarked) { - writable.write('M') - } else { - writable.write(' ') - } - - if (isGroup(this.item)) { - writable.write('G') - } else if (record.downloading) { - writable.write(braille[Math.floor(Date.now() / 250) % 6]) - } else if (record.playing) { - writable.write('\u25B6') - } else { - writable.write(' ') - } - - writable.write(' ') - } - - get isMarked() { - return this.recordStore.app.editMode && this.recordStore.app.markGrouplike.items.includes(this.item) + // Add a couple spaces anyway. This is less than the padding of the tatus + // text of elements which represent real playlist items; that's to + // distinguish "fake" rows from actual playlist items. + writable.write(' ') + this.drawX += 2 } - get isReal() { - return !this.item.fake + keyPressed(keyBuf) { + // This function is overridden by InteractiveGrouplikeItemElement, but + // it's still specified here that only enter counts as an action key. + // By default for buttons, the space key also works, but since in this app + // space is generally bound to mean "pause" instead of "select", we don't + // check if space is pressed here. + if (telc.isEnter(keyBuf)) { + this.emit('pressed') + if (isGroup(this.item)) { + this.emit('browse') + } + } } +} - get isGroup() { - return isGroup(this.item) && this.isReal +class InteractiveGrouplikeItemElement extends BasicGrouplikeItemElement { + constructor(item, recordStore) { + super(item.name) + this.item = item + this.recordStore = recordStore } - get isTrack() { - return isTrack(this.item) && this.isReal + drawTo(writable) { + this.text = this.item.name + super.drawTo(writable) } keyPressed(keyBuf) { @@ -1081,7 +1064,7 @@ class GrouplikeItemElement extends Button { editMode && this.isReal && {label: this.isMarked ? 'Unmark' : 'Mark', action: () => this.emit('mark')}, anyMarked && this.isReal && {label: 'Paste (above)', action: () => this.emit('paste', {where: 'above'})}, anyMarked && this.isReal && {label: 'Paste (below)', action: () => this.emit('paste', {where: 'below'})}, - anyMarked && !this.isReal && {label: 'Paste', action: () => this.emit('paste')}, // No "above" or "elow" in the label because the fake item will be replaced (it'll disappear, since there'll be an item in the group) + anyMarked && !this.isReal && {label: 'Paste', action: () => this.emit('paste')}, // No "above" or "elow" in the label because the "fake" item/row will be replaced (it'll disappear, since there'll be an item in the group) this.isReal && {label: 'Play', action: () => this.emit('queue', {where: 'next', play: true})}, this.isReal && {label: 'Play next', action: () => this.emit('queue', {where: 'next'})}, this.isReal && {label: 'Play at end', action: () => this.emit('queue', {where: 'end'})}, @@ -1092,6 +1075,52 @@ class GrouplikeItemElement extends Button { }) } } + + writeStatus(writable) { + if (isGroup(this.item)) { + // The ANSI attributes here will apply to the rest of the line, too. + // (We don't reset the active attributes until after drawing the rest of + // the line.) + writable.write(ansi.setAttributes([ansi.C_BLUE, ansi.A_BRIGHT])) + } + + this.drawX += 3 + + const braille = '⠈⠐⠠⠄⠂⠁' + const brailleChar = braille[Math.floor(Date.now() / 250) % 6] + + const record = this.recordStore.getRecord(this.item) + + if (this.isMarked) { + writable.write('M') + } else { + writable.write(' ') + } + + if (isGroup(this.item)) { + writable.write('G') + } else if (record.downloading) { + writable.write(braille[Math.floor(Date.now() / 250) % 6]) + } else if (record.playing) { + writable.write('\u25B6') + } else { + writable.write(' ') + } + + writable.write(' ') + } + + get isMarked() { + return this.recordStore.app.editMode && this.recordStore.app.markGrouplike.items.includes(this.item) + } + + get isGroup() { + return isGroup(this.item) + } + + get isTrack() { + return isTrack(this.item) + } } class ListingJumpElement extends Form { -- cgit 1.3.0-6-gf8a5