diff options
-rw-r--r-- | todo.txt | 6 | ||||
-rw-r--r-- | ui.js | 100 |
2 files changed, 85 insertions, 21 deletions
diff --git a/todo.txt b/todo.txt index 4c93789..bfb6e98 100644 --- a/todo.txt +++ b/todo.txt @@ -696,3 +696,9 @@ TODO: Pressing escape while you've got items selected should deselect those still handy to not be locked out of stopping playback altogether. Alternative: clear the selection (without stopping playback) only if the cursor is currently on a selected item. + +TODO: When you're navigating down (or up) a menu, if that menu's got a + scrollbar *and* is divided into sections, passing a divider line should + try to scroll the whole newly active section into view! This way you get + all the context and don't miss out on realizing there are more items + below. (This only applies for keyboard navigation, not wheel scrolling.) diff --git a/ui.js b/ui.js index f006a70..3e0098f 100644 --- a/ui.js +++ b/ui.js @@ -45,6 +45,7 @@ import { parentSymbol, reverseOrderOfGroups, searchForItem, + selectTracks, shuffleOrderOfGroups, } from './playlist-utils.js' @@ -317,6 +318,15 @@ export default class AppElement extends FocusElement { {value: 'normal', label: 'In order'} ], this.showContextMenu) + this.selectGrouplikeItemsControl = new InlineListPickerElement('Which?', [ + {value: 'all', label: 'all tracks'}, + {value: '1', label: 'one track, randomly'}, + {value: '4', label: 'four tracks, randomly'}, + {value: '25%', label: '25% of the tracks, randomly'}, + {value: '50%', label: '50% of the tracks, randomly'}, + {value: '75%', label: '75% of the tracks, randomly'}, + ], this.showContextMenu) + this.menubar.buildItems([ {text: 'mtui', menuItems: [ {label: 'mtui (perpetual development)'}, @@ -914,32 +924,79 @@ export default class AppElement extends FocusElement { this.emitMarkChanged() } - markItem(item) { - if (isGroup(item)) { - for (const child of item.items) { - this.markItem(child) + selectTracksForMarking(item, forUnmarking = false, useGroupSelectionControl = false) { + let mode = 'all' + let modeValue = 0 + + if (useGroupSelectionControl) { + const value = this.selectGrouplikeItemsControl.curValue + if (value === 'all') { + // Do nothing, this is the default + } else if (value.endsWith('%')) { + mode = 'random' + modeValue = value + } else if (parseInt(value).toString() === value) { + mode = 'random' + modeValue = value } + } + + const range = { + items: + (flattenGrouplike(item) + .items + .filter(isTrack) + .filter(track => + this.markGrouplike.items.includes(track) === forUnmarking)) + } + + return selectTracks(range, mode, modeValue).items + } + + markItem(item, useGroupSelectionControl = false) { + const { items: mark } = this.markGrouplike + + let add + if (isGroup(item)) { + add = this.selectTracksForMarking(item, false, useGroupSelectionControl) + } else if (mark.includes(item)) { + add = [] } else { - const { items } = this.markGrouplike - if (!items.includes(item)) { - items.push(item) - this.emitMarkChanged() - } + add = [item] + } + + if (!add.length) { + return } + + for (const track of add) { + mark.push(track) + } + + this.emitMarkChanged() } - unmarkItem(item) { + unmarkItem(item, useGroupSelectionControl) { + const { items: mark } = this.markGrouplike + + let remove if (isGroup(item)) { - for (const child of item.items) { - this.unmarkItem(child) - } + remove = this.selectTracksForMarking(item, true, useGroupSelectionControl) + } else if (mark.includes(item)) { + remove = [item] } else { - const { items } = this.markGrouplike - if (items.includes(item)) { - items.splice(items.indexOf(item), 1) - this.emitMarkChanged() - } + remove = [] } + + if (!remove.length) { + return + } + + for (const track of remove) { + mark.splice(mark.indexOf(track), 1) + } + + this.emitMarkChanged() } getMarkStatus(item) { @@ -1442,9 +1499,10 @@ export default class AppElement extends FocusElement { ...(item === this.markGrouplike ? [{label: 'Deselect all', action: () => this.unmarkAll()}] : [ - this.getMarkStatus(item) !== 'unmarked' && {label: 'Remove from selection', action: () => this.unmarkItem(item)}, - this.getMarkStatus(item) !== 'marked' && {label: 'Add to selection', action: () => this.markItem(item)} - ]) + isGroup(item) && {element: this.selectGrouplikeItemsControl}, + this.getMarkStatus(item) !== 'unmarked' && {label: 'Remove from selection', action: () => this.unmarkItem(item, true)}, + this.getMarkStatus(item) !== 'marked' && {label: 'Add to selection', action: () => this.markItem(item, true)}, + ]) ] } } |