aboutsummaryrefslogtreecommitdiff
path: root/ext/js/display
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2021-03-25 19:22:34 -0400
committerGitHub <noreply@github.com>2021-03-25 19:22:34 -0400
commite7035dcff41d94f20c0bc8865d413412afc7c229 (patch)
tree41ba5eafbb5d0d50d94eea4d6d6775486edc0856 /ext/js/display
parentcda04b576db3ba058c315be606d38dcacca2a8f6 (diff)
Enable audio menu shift click (#1555)
* Expose modifier keys * Add updateMenuItems * Don't close menu if shift key is held * Add _createMenuItems * Simplification * Maintain a list of open popup menus * Expose expression/reading * Reuse existing items * Update menu after a cache update * Update menu position
Diffstat (limited to 'ext/js/display')
-rw-r--r--ext/js/display/display-audio.js99
1 files changed, 78 insertions, 21 deletions
diff --git a/ext/js/display/display-audio.js b/ext/js/display/display-audio.js
index f1feff5c..bb88c89e 100644
--- a/ext/js/display/display-audio.js
+++ b/ext/js/display/display-audio.js
@@ -31,6 +31,7 @@ class DisplayAudio {
this._cache = new Map();
this._menuContainer = document.querySelector('#popup-menus');
this._entriesToken = {};
+ this._openMenus = new Set();
}
get autoPlayAudioDelay() {
@@ -198,9 +199,12 @@ class DisplayAudio {
}
_onAudioPlayMenuCloseClick(definitionIndex, expressionIndex, e) {
- const {detail: {action, item, menu}} = e;
+ const {detail: {action, item, menu, shiftKey}} = e;
switch (action) {
case 'playAudioFromSource':
+ if (shiftKey) {
+ e.preventDefault();
+ }
this._playAudioFromSource(definitionIndex, expressionIndex, item);
break;
case 'setPrimaryAudio':
@@ -306,12 +310,14 @@ class DisplayAudio {
for (let i = 0, ii = sources.length; i < ii; ++i) {
const source = sources[i];
+ let cacheUpdated = false;
let infoListPromise;
let sourceInfo = sourceMap.get(source);
if (typeof sourceInfo === 'undefined') {
infoListPromise = this._getExpressionAudioInfoList(source, expression, reading, details);
sourceInfo = {infoListPromise, infoList: null};
sourceMap.set(source, sourceInfo);
+ cacheUpdated = true;
}
let {infoList} = sourceInfo;
@@ -332,14 +338,17 @@ class DisplayAudio {
}
}
- const audio = await this._createAudioFromInfoList(source, infoList, start, end);
- if (audio !== null) { return audio; }
+ const {result, cacheUpdated: cacheUpdated2} = await this._createAudioFromInfoList(source, infoList, start, end);
+ if (cacheUpdated || cacheUpdated2) { this._updateOpenMenu(); }
+ if (result !== null) { return result; }
}
return null;
}
async _createAudioFromInfoList(source, infoList, start, end) {
+ let result = null;
+ let cacheUpdated = false;
for (let i = start; i < end; ++i) {
const item = infoList[i];
@@ -352,6 +361,8 @@ class DisplayAudio {
item.audioPromise = audioPromise;
}
+ cacheUpdated = true;
+
try {
audio = await audioPromise;
} catch (e) {
@@ -363,11 +374,12 @@ class DisplayAudio {
item.audio = audio;
}
- if (audio === null) { continue; }
-
- return {audio, source, infoListIndex: i};
+ if (audio !== null) {
+ result = {audio, source, infoListIndex: i};
+ break;
+ }
}
- return null;
+ return {result, cacheUpdated};
}
async _createAudioFromInfo(info, source) {
@@ -471,7 +483,13 @@ class DisplayAudio {
const {expression, reading} = expressionReading;
const popupMenu = this._createMenu(button, expression, reading);
+ this._openMenus.add(popupMenu);
popupMenu.prepare();
+ popupMenu.on('close', this._onPopupMenuClose.bind(this));
+ }
+
+ _onPopupMenuClose({menu}) {
+ this._openMenus.delete(menu);
}
_sourceIsDownloadable(source) {
@@ -533,21 +551,36 @@ class DisplayAudio {
}
_createMenu(sourceButton, expression, reading) {
- // Options
- const sources = this._getAudioSources(this._getAudioOptions());
-
// Create menu
- const {displayGenerator} = this._display;
- const menuNode = displayGenerator.instantiateTemplate('audio-button-popup-menu');
- const menuBodyNode = menuNode.querySelector('.popup-menu-body');
+ const menuContainerNode = this._display.displayGenerator.instantiateTemplate('audio-button-popup-menu');
+ const menuBodyNode = menuContainerNode.querySelector('.popup-menu-body');
+ menuContainerNode.dataset.expression = expression;
+ menuContainerNode.dataset.reading = reading;
// Set up items based on options and cache data
+ this._createMenuItems(menuContainerNode, menuBodyNode, expression, reading);
+
+ // Update primary card audio display
+ this._updateMenuPrimaryCardAudio(menuBodyNode, expression, reading);
+
+ // Create popup menu
+ this._menuContainer.appendChild(menuContainerNode);
+ return new PopupMenu(sourceButton, menuContainerNode);
+ }
+
+ _createMenuItems(menuContainerNode, menuItemContainer, expression, reading) {
+ const sources = this._getAudioSources(this._getAudioOptions());
+ const {displayGenerator} = this._display;
let showIcons = false;
+ const currentItems = [...menuItemContainer.children];
for (const {source, displayName, isInOptions, downloadable} of sources) {
const entries = this._getMenuItemEntries(source, expression, reading);
for (let i = 0, ii = entries.length; i < ii; ++i) {
const {valid, index, name} = entries[i];
- const node = displayGenerator.instantiateTemplate('audio-button-popup-menu-item');
+ let node = this._getOrCreateMenuItem(currentItems, source, index);
+ if (node === null) {
+ node = displayGenerator.instantiateTemplate('audio-button-popup-menu-item');
+ }
const labelNode = node.querySelector('.popup-menu-item-audio-button .popup-menu-item-label');
let label = displayName;
@@ -571,17 +604,32 @@ class DisplayAudio {
node.dataset.sourceInOptions = `${isInOptions}`;
node.dataset.downloadable = `${downloadable}`;
- menuBodyNode.appendChild(node);
+ menuItemContainer.appendChild(node);
}
}
- menuNode.dataset.showIcons = `${showIcons}`;
+ for (const node of currentItems) {
+ const {parentNode} = node;
+ if (parentNode === null) { continue; }
+ parentNode.removeChild(node);
+ }
+ menuContainerNode.dataset.showIcons = `${showIcons}`;
+ }
- // Update primary card audio display
- this._updateMenuPrimaryCardAudio(menuBodyNode, expression, reading);
+ _getOrCreateMenuItem(currentItems, source, index) {
+ if (index === null) { index = 0; }
+ index = `${index}`;
+ for (let i = 0, ii = currentItems.length; i < ii; ++i) {
+ const node = currentItems[i];
+ if (source !== node.dataset.source) { continue; }
- // Create popup menu
- this._menuContainer.appendChild(menuNode);
- return new PopupMenu(sourceButton, menuNode);
+ let index2 = node.dataset.index;
+ if (typeof index2 === 'undefined') { index2 = '0'; }
+ if (index !== index2) { continue; }
+
+ currentItems.splice(i, 1);
+ return node;
+ }
+ return null;
}
_getMenuItemEntries(source, expression, reading) {
@@ -631,4 +679,13 @@ class DisplayAudio {
node.dataset.isPrimaryCardAudio = `${isPrimaryCardAudio}`;
}
}
+
+ _updateOpenMenu() {
+ for (const menu of this._openMenus) {
+ const menuContainerNode = menu.containerNode;
+ const {expression, reading} = menuContainerNode.dataset;
+ this._createMenuItems(menuContainerNode, menu.bodyNode, expression, reading);
+ menu.updatePosition();
+ }
+ }
}