summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2021-09-30 19:31:45 -0400
committerGitHub <noreply@github.com>2021-09-30 19:31:45 -0400
commit55a7e7f9a8bf50a4891f2e4d0031b968aa0c25b0 (patch)
tree9e22b839a0829e7747b0101d5884ade24bf2bb84
parentb0f6c41f5dc7f498f2948f846dd273bcb1bc1f0b (diff)
Hotkey dictionary navigation fix (#1970)
* Add getRect function to ScrollElement * Update _focusEntry to take a definition index * Update behaviour of (next|previous)EntryDifferentDictionary
-rw-r--r--ext/js/display/display.js91
-rw-r--r--ext/js/dom/scroll-element.js4
2 files changed, 71 insertions, 24 deletions
diff --git a/ext/js/display/display.js b/ext/js/display/display.js
index 0e82af54..5e35a9de 100644
--- a/ext/js/display/display.js
+++ b/ext/js/display/display.js
@@ -116,8 +116,8 @@ class Display extends EventDispatcher {
['close', () => { this._onHotkeyClose(); }],
['nextEntry', this._onHotkeyActionMoveRelative.bind(this, 1)],
['previousEntry', this._onHotkeyActionMoveRelative.bind(this, -1)],
- ['lastEntry', () => { this._focusEntry(this._dictionaryEntries.length - 1, true); }],
- ['firstEntry', () => { this._focusEntry(0, true); }],
+ ['lastEntry', () => { this._focusEntry(this._dictionaryEntries.length - 1, 0, true); }],
+ ['firstEntry', () => { this._focusEntry(0, 0, true); }],
['historyBackward', () => { this._sourceTermView(); }],
['historyForward', () => { this._nextTermView(); }],
['playAudio', () => { this._playAudioCurrent(); }],
@@ -756,7 +756,7 @@ class Display extends EventDispatcher {
_onWheel(e) {
if (e.altKey) {
if (e.deltaY !== 0) {
- this._focusEntry(this._index + (e.deltaY > 0 ? 1 : -1), true);
+ this._focusEntry(this._index + (e.deltaY > 0 ? 1 : -1), 0, true);
e.preventDefault();
}
} else if (e.shiftKey) {
@@ -997,7 +997,7 @@ class Display extends EventDispatcher {
this._displayAnki.setupEntry(entry, i);
container.appendChild(entry);
if (focusEntry === i) {
- this._focusEntry(i, false);
+ this._focusEntry(i, 0, false);
}
this._elementOverflowController.addElements(entry);
@@ -1112,15 +1112,21 @@ class Display extends EventDispatcher {
}
this._index = index;
-
- return entry;
}
- _focusEntry(index, smooth) {
+ _focusEntry(index, definitionIndex, smooth) {
index = Math.max(Math.min(index, this._dictionaryEntries.length - 1), 0);
- const entry = this._entrySetCurrent(index);
- let target = index === 0 || entry === null ? 0 : this._getElementTop(entry);
+ this._entrySetCurrent(index);
+
+ let node = (index >= 0 && index < this._dictionaryEntryNodes.length ? this._dictionaryEntryNodes[index] : null);
+ if (definitionIndex > 0) {
+ const definitionNodes = this._getDictionaryEntryDefinitionNodes(index);
+ if (definitionIndex < definitionNodes.length) {
+ node = definitionNodes[definitionIndex];
+ }
+ }
+ let target = (index === 0 && definitionIndex <= 0) || node === null ? 0 : this._getElementTop(node);
if (this._navigationHeader !== null) {
target -= this._navigationHeader.getBoundingClientRect().height;
@@ -1135,31 +1141,68 @@ class Display extends EventDispatcher {
}
_focusEntryWithDifferentDictionary(offset, smooth) {
- const offsetSign = Math.sign(offset);
- if (offsetSign === 0) { return false; }
+ const sign = Math.sign(offset);
+ if (sign === 0) { return false; }
let index = this._index;
- const dictionaryEntryCount = this._dictionaryEntries.length;
- if (index < 0 || index >= dictionaryEntryCount) { return false; }
-
- const {dictionary} = this._dictionaryEntries[index];
- for (let indexNext = index + offsetSign; indexNext >= 0 && indexNext < dictionaryEntryCount; indexNext += offsetSign) {
- const {dictionaryNames} = this._dictionaryEntries[indexNext];
- if (dictionaryNames.length > 1 || !dictionaryNames.includes(dictionary)) {
- offset -= offsetSign;
- if (Math.sign(offsetSign) !== offset) {
- index = indexNext;
+ const count = Math.min(this._dictionaryEntries.length, this._dictionaryEntryNodes.length);
+ if (index < 0 || index >= count) { return false; }
+
+ const dictionaryEntry = this._dictionaryEntries[index];
+ const visibleDefinitionIndex = this._getDictionaryEntryVisibleDefinitionIndex(index, sign);
+ if (visibleDefinitionIndex === null) { return false; }
+
+ const {dictionary} = dictionaryEntry.definitions[visibleDefinitionIndex];
+ let focusDefinitionIndex = null;
+ for (let i = index; i >= 0 && i < count; i += sign) {
+ const {definitions} = this._dictionaryEntries[i];
+ const jj = definitions.length;
+ let j = (i === index ? visibleDefinitionIndex + sign : (sign > 0 ? 0 : jj - 1));
+ for (; j >= 0 && j < jj; j += sign) {
+ if (definitions[j].dictionary !== dictionary) {
+ focusDefinitionIndex = j;
+ index = i;
+ i = -2; // Terminate outer loop
break;
}
}
}
- if (index === this._index) { return false; }
+ if (focusDefinitionIndex === null) { return false; }
- this._focusEntry(index, smooth);
+ this._focusEntry(index, focusDefinitionIndex, smooth);
return true;
}
+ _getDictionaryEntryVisibleDefinitionIndex(index, sign) {
+ const {top: scrollTop, bottom: scrollBottom} = this._windowScroll.getRect();
+
+ const {definitions} = this._dictionaryEntries[index];
+ const nodes = this._getDictionaryEntryDefinitionNodes(index);
+ const definitionCount = Math.min(definitions.length, nodes.length);
+ if (definitionCount <= 0) { return null; }
+
+ let visibleIndex = null;
+ let visibleCoverage = 0;
+ for (let i = (sign > 0 ? 0 : definitionCount - 1); i >= 0 && i < definitionCount; i += sign) {
+ const {top, bottom} = nodes[i].getBoundingClientRect();
+ if (bottom <= scrollTop || top >= scrollBottom) { continue; }
+ const top2 = Math.max(scrollTop, Math.min(scrollBottom, top));
+ const bottom2 = Math.max(scrollTop, Math.min(scrollBottom, bottom));
+ const coverage = (bottom2 - top2) / (bottom - top);
+ if (coverage >= visibleCoverage) {
+ visibleCoverage = coverage;
+ visibleIndex = i;
+ }
+ }
+
+ return visibleIndex !== null ? visibleIndex : (sign > 0 ? definitionCount - 1 : 0);
+ }
+
+ _getDictionaryEntryDefinitionNodes(index) {
+ return this._dictionaryEntryNodes[index].querySelectorAll('.definition-item');
+ }
+
_sourceTermView() {
this._relativeTermView(false);
}
@@ -1529,7 +1572,7 @@ class Display extends EventDispatcher {
let count = Number.parseInt(argument, 10);
if (!Number.isFinite(count)) { count = 1; }
count = Math.max(0, Math.floor(count));
- this._focusEntry(this._index + count * sign, true);
+ this._focusEntry(this._index + count * sign, 0, true);
}
_onHotkeyActionPlayAudioFromSource(source) {
diff --git a/ext/js/dom/scroll-element.js b/ext/js/dom/scroll-element.js
index 173ce499..7db3026a 100644
--- a/ext/js/dom/scroll-element.js
+++ b/ext/js/dom/scroll-element.js
@@ -68,6 +68,10 @@ class ScrollElement {
this._animationRequestId = null;
}
+ getRect() {
+ return this._node.getBoundingClientRect();
+ }
+
// Private
_onAnimationFrame(time) {