vpat 3: consistent focus order in all directions (#3953)

On last tab in the itemPane/contextPane, focus will wrap around
to the selected tab to complete the loop. It ensures
that the focus order is consistent regardless of the
directions.

Also, minor tweaks to focusWrapAround in Zotero_Tabs
to properly move focus to the last focusable entry in
the contextPane.
This commit is contained in:
abaevbog 2024-04-12 06:26:47 -04:00 committed by GitHub
parent 4432f88f1d
commit 9421e09e84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 25 deletions

View file

@ -749,36 +749,41 @@ var Zotero_Tabs = new function () {
// Used to move focus back to itemTree or contextPane from the tabs.
this.focusWrapAround = function () {
// Focus the last field of contextPane when reader is opened
if (Zotero_Tabs.selectedIndex > 0) {
Services.focus.moveFocus(window, document.getElementById("zotero-context-pane-sidenav"),
Services.focus.MOVEFOCUS_BACKWARD, 0);
return;
}
// If no item is selected, focus items list.
if (ZoteroPane.itemPane.mode == "message") {
document.getElementById("item-tree-main-default").focus();
return;
}
else {
let selected = ZoteroPane.getSelectedItems();
// If the selected collection row is duplicates, just focus on the
// itemTree until the merge pane is keyboard accessible
// If multiple items selected, focus on itemTree as well.
let collectionRow = ZoteroPane.collectionsView.selectedTreeRow;
if (collectionRow.isDuplicates() || selected.length !== 1) {
document.getElementById("item-tree-main-default").focus();
return;
}
// Special treatment for notes and attachments in itemPane
selected = selected[0];
if (selected.isNote()) {
document.getElementById("zotero-note-editor").focus();
return;
}
if (selected.isAttachment()) {
document.getElementById("attachment-note-editor").focus();
return;
}
// For regular items, focus the last field
// We do that by moving focus backwards from the element following the pane, because Services.focus doesn't
// support MOVEFOCUS_LAST on subtrees
Services.focus.moveFocus(window, document.getElementById('zotero-context-splitter'),
Services.focus.MOVEFOCUS_BACKWARD, 0);
let selected = ZoteroPane.getSelectedItems();
// If the selected collection row is duplicates, just focus on the
// itemTree until the merge pane is keyboard accessible
// If multiple items selected, focus on itemTree as well.
let collectionRow = ZoteroPane.collectionsView.selectedTreeRow;
if (collectionRow.isDuplicates() || selected.length !== 1) {
document.getElementById("item-tree-main-default").focus();
return;
}
// Special treatment for notes and attachments in itemPane
selected = selected[0];
if (selected.isNote()) {
document.getElementById("zotero-note-editor").focus();
return;
}
if (selected.isAttachment()) {
document.getElementById("attachment-note-editor").focus();
return;
}
// For regular items, focus the last field
// We do that by moving focus backwards from the element following the pane, because Services.focus doesn't
// support MOVEFOCUS_LAST on subtrees
Services.focus.moveFocus(window, document.getElementById("zotero-context-splitter"),
Services.focus.MOVEFOCUS_BACKWARD, 0);
};
/**

View file

@ -39,6 +39,7 @@ var ZoteroPane = new function()
this.__defineGetter__('loaded', function () { return _loaded; });
var _lastSelectedItems = [];
var lastFocusedElement = null;
this.lastKeyPress = null;
//Privileged methods
this.destroy = destroy;
@ -879,6 +880,7 @@ var ZoteroPane = new function()
* E.g. tab navigation hotkeys should work regardless of which component is focused.
*/
function captureKeyDown(event) {
ZoteroPane.lastKeyPress = (event.shiftKey ? "Shift" : "") + event.key;
const cmdOrCtrlOnly = Zotero.isMac
? (event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey)
: (event.ctrlKey && !event.shiftKey && !event.altKey);
@ -1102,6 +1104,29 @@ var ZoteroPane = new function()
}
this.handleBlur = (event) => {
// If one tabs through the item/context pane all the way to the end and
// the focus leaves the pane, wrap it around to refocus the selected tab
let itemPane = document.getElementById("zotero-item-pane");
let contextPane = document.getElementById("zotero-context-pane");
let loosingFocus = event.target;
let receivingFocus = event.relatedTarget;
let itemPaneLostFocus = itemPane.contains(loosingFocus) && !itemPane.contains(receivingFocus);
let contextPaneLostFocus = contextPane.contains(loosingFocus) && !contextPane.contains(receivingFocus);
// Do not do anything if the window lost focus or if the last
// keypress was anything but a Tab. That way, it won't interfere with other navigation such as
// Shift-tab from the header into the itemsView.
if (Services.focus.activeWindow === window && this.lastKeyPress === "Tab"
&& (itemPaneLostFocus || contextPaneLostFocus)) {
if (receivingFocus) {
Zotero_Tabs.moveFocus("current");
}
// event.relatedTarget is null when the reader is opened and we need a small
// delay otherwise the focus lands within the reader
else {
setTimeout(() => Zotero_Tabs.moveFocus("current"));
}
this.lastKeyPress = null;
}
// When focus shifts, unless we are inside of a panel, save
// the last focused element to be able to return focus to it when the panel closes
if (!event.target.closest("panel")) {