Hotkey dictionary navigation fix (#1970)

* Add getRect function to ScrollElement

* Update _focusEntry to take a definition index

* Update behaviour of (next|previous)EntryDifferentDictionary
This commit is contained in:
toasted-nutbread 2021-09-30 19:31:45 -04:00 committed by GitHub
parent b0f6c41f5d
commit 55a7e7f9a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 23 deletions

View File

@ -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 count = Math.min(this._dictionaryEntries.length, this._dictionaryEntryNodes.length);
if (index < 0 || index >= count) { 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 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) {

View File

@ -68,6 +68,10 @@ class ScrollElement {
this._animationRequestId = null;
}
getRect() {
return this._node.getBoundingClientRect();
}
// Private
_onAnimationFrame(time) {