From 8feefa78118cdfe9a98198786a0315053dc098a1 Mon Sep 17 00:00:00 2001 From: Florrie Date: Wed, 17 Apr 2019 10:40:56 -0300 Subject: Rework input system, support number pad Also changed focus main/queue listings to [ and ], so that we can use the 2 key for the down arrow on a number pad. --- README.md | 15 ++++++-- ui.js | 128 ++++++++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 112 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index d66a65b..1d444b0 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ You're also welcome to share any ideas, suggestions, and questions through there * Up and Down - select previous or next item in a listing * Tab and Shift+Tab - switch between UI elements * g and G (shift+G) - move to the first and last item in a listing -* 1 - focus the main track/group listing -* 2 - focus the queue listing +* [ - focus the main track/group listing +* ] - focus the queue listing * Enter - play the selected track * Shift+Up or p - play previous track * Shift+Down or n - play next track @@ -37,6 +37,7 @@ You're also welcome to share any ideas, suggestions, and questions through there * l - toggle track loop * Right - seek ahead * Left - seek back +* m or f - open a menu of actions related to the selected track or group * v and V - increase and decrease playback volume * Ctrl+F or / - jump to a track or group by entering (part of) its name * Ctrl+O - 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`) @@ -46,7 +47,6 @@ You're also welcome to share any ideas, suggestions, and questions through there * **In the main listing:** * Enter - if the selected item is a group, enter it; if it's a track, play it * Backspace - leave the current group (if in one) - * m or f - open a menu of actions related to the selected track or group * q - queue the selected track or group to play after any other items in the queue (usually after the current track) * Q (shift+Q) - queue the selected track or group to play immediately after what's currently playing * d - download the selected track (but don't play it) @@ -62,3 +62,12 @@ You're also welcome to share any ideas, suggestions, and questions through there * All the usual controls (up, down, tab, space, enter) work * Backspace or Escape - close the menu without taking any action * Type the beginning of the name of an option, like its first letter, to quickly jump to it +* **With a number pad:** + * Should be in NumLock! + * 2, 4, 6, 8 - cursor keys (down/left/right/up) + * 5 - pause (and any action that would take the space bar) + * 7, 9 - focus main track/group, queue listings + * /, \* - decrease, increase playback volume + * . - back (and any action that would take backspace) + * + - open a context menu + * 1, 3 - skip to previous, next track diff --git a/ui.js b/ui.js index e9128a3..049930b 100644 --- a/ui.js +++ b/ui.js @@ -35,18 +35,90 @@ const { promisify } = require('util') const readFile = promisify(fs.readFile) const writeFile = promisify(fs.writeFile) -// Sneaky hack :) -const addKey = (prop, key) => { - const oldFunc = telc[prop] - telc[prop] = input => input.toString().toLowerCase() === key || oldFunc(input) +const input = {} + +const keyBindings = [ + ['isUp', telc.isUp], + ['isDown', telc.isDown], + ['isLeft', telc.isLeft], + ['isRight', telc.isRight], + ['isSelect', telc.isSelect], + ['isBackspace', telc.isBackspace], + ['isMenu', 'm'], + ['isMenu', 'f'], + ['isTogglePause', telc.isSpace], + ['isToggleLoop', 'l'], + ['isStop', telc.isEscape], + ['isVolumeUp', 'v', {caseless: false}], + ['isVolumeDown', 'V', {caseless: false}], + ['isSkipBack', telc.isShiftUp], + ['isSkipAhead', telc.isShiftDown], + ['isSkipBack', 'p'], + ['isSkipAhead', 'n'], + ['isFocusTabber', '['], + ['isFocusQueue', ']'], + ['isNextTab', 't', {caseless: false}], + ['isPreviousTab', 'T', {caseless: false}], + ['isDownload', 'd'], + ['isRemove', 'x'], + ['isQueueAtEnd', 'q', {caseless: false}], + ['isQueueAtStart', 'Q', {caseless: false}], + ['isShuffleQueue', 's'], + ['isClearQueue', 'c'], + + // Number pad + ['isUp', '8'], + ['isDown', '2'], + ['isLeft', '4'], + ['isRight', '6'], + ['isSpace', '5'], + ['isTogglePause', '5'], + ['isBackspace', '.'], + ['isMenu', '+'], + ['isSkipBack', '1'], + ['isSkipAhead', '3'], + ['isVolumeDown', '/'], + ['isVolumeUp', '*'], + ['isFocusTabber', '7'], + ['isFocusQueue', '9'], + + // HJKL + ['isDown', 'j'], + ['isUp', 'k'], + // Don't use these for now... currently L is used for toggling loop. + // May want to look into changing that (so we can re-enable these). + // ['isLeft', 'h'], + // ['isRight', 'l'], +] + +const addKey = (prop, keyOrFunc, {caseless = true} = {}) => { + const oldFunc = input[prop] || (() => false) + let newFunc + if (typeof keyOrFunc === 'function') { + newFunc = keyOrFunc + } else if (typeof keyOrFunc === 'string') { + const key = keyOrFunc + if (caseless) { + newFunc = input => input.toString().toLowerCase() === key.toLowerCase() + } else { + newFunc = input => input.toString() === key + } + } + input[prop] = keyBuf => newFunc(keyBuf) || oldFunc(keyBuf) } -addKey('isDown', 'j') -addKey('isUp', 'k') -// Don't use these for now... currently L is used for toggling loop. -// May want to look into changing that (so we can re-enable these). -// addKey('isLeft', 'h') -// addKey('isRight', 'l') +for (const entry of keyBindings) { + addKey(...entry) +} + +// Some things just need to be overridden in order for the rest of tui-lib to +// recognize our new keys. +telc.isUp = input.isUp +telc.isDown = input.isDown +telc.isLeft = input.isLeft +telc.isRight = input.isRight +telc.isSelect = input.isSelect +telc.isBackspace = input.isBackspace class AppElement extends FocusElement { constructor() { @@ -434,27 +506,27 @@ class AppElement extends FocusElement { return } - if (telc.isRight(keyBuf)) { + if (input.isRight(keyBuf)) { this.seekAhead(10) - } else if (telc.isLeft(keyBuf)) { + } else if (input.isLeft(keyBuf)) { this.seekBack(10) - } else if (telc.isSpace(keyBuf)) { + } else if (input.isTogglePause(keyBuf)) { this.togglePause() - } else if (telc.isCaselessLetter(keyBuf, 'l')) { + } else if (input.isToggleLoop(keyBuf)) { this.toggleLoop() - } else if (telc.isCharacter(keyBuf, 'v')) { + } else if (input.isVolumeUp(keyBuf)) { this.volUp() - } else if (telc.isCharacter(keyBuf, 'V')) { + } else if (input.isVolumeDown(keyBuf)) { this.volDown() - } else if (telc.isEscape(keyBuf)) { + } else if (input.isStop(keyBuf)) { this.clearPlayingTrack() - } else if (telc.isShiftUp(keyBuf) || telc.isCaselessLetter(keyBuf, 'p')) { + } else if (input.isSkipBack(keyBuf)) { this.playPreviousTrack(this.playingTrack, true) - } else if (telc.isShiftDown(keyBuf) || telc.isCaselessLetter(keyBuf, 'n')) { + } else if (input.isSkipAhead(keyBuf)) { this.playNextTrack(this.playingTrack, true) - } else if (telc.isCharacter(keyBuf, '1') && this.tabber.selectable) { + } else if (input.isFocusTabber(keyBuf) && this.tabber.selectable) { this.root.select(this.tabber) - } else if (telc.isCharacter(keyBuf, '2') && this.queueListingElement.selectable) { + } else if (input.isFocusQueue(keyBuf) && this.queueListingElement.selectable) { this.root.select(this.queueListingElement) } else if (keyBuf.equals(Buffer.from([5]))) { // Ctrl-E this.editMode = !this.editMode @@ -1597,11 +1669,11 @@ class InteractiveGrouplikeItemElement extends BasicGrouplikeItemElement { } keyPressed(keyBuf) { - if (telc.isCaselessLetter(keyBuf, 'd')) { + if (input.isDownload(keyBuf)) { this.emit('download') - } else if (telc.isCharacter(keyBuf, 'q')) { + } else if (input.isQueueAtEnd(keyBuf)) { this.emit('queue', {where: 'end'}) - } else if (telc.isCharacter(keyBuf, 'Q')) { + } else if (input.isQueueAtStart(keyBuf)) { this.emit('queue', {where: 'next'}) } else if (telc.isEnter(keyBuf)) { if (isGroup(this.item)) { @@ -1609,9 +1681,9 @@ class InteractiveGrouplikeItemElement extends BasicGrouplikeItemElement { } else { this.emit('queue', {where: 'next', play: true}) } - } else if (telc.isCaselessLetter(keyBuf, 'x')) { + } else if (input.isRemove(keyBuf)) { this.emit('remove') - } else if (telc.isCaselessLetter(keyBuf, 'm') || telc.isCaselessLetter(keyBuf, 'f')) { + } else if (input.isMenu(keyBuf)) { this.emit('menu', this) } } @@ -1800,9 +1872,9 @@ class QueueListingElement extends GrouplikeListingElement { } keyPressed(keyBuf) { - if (telc.isCaselessLetter(keyBuf, 's')) { + if (input.isShuffleQueue(keyBuf)) { this.emit('shuffle') - } else if (telc.isCaselessLetter(keyBuf, 'c')) { + } else if (input.isClearQueue(keyBuf)) { this.emit('clear') } else { return super.keyPressed(keyBuf) -- cgit 1.3.0-6-gf8a5