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,11 +749,17 @@ var Zotero_Tabs = new function () {
// Used to move focus back to itemTree or contextPane from the tabs. // Used to move focus back to itemTree or contextPane from the tabs.
this.focusWrapAround = function () { 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 no item is selected, focus items list.
if (ZoteroPane.itemPane.mode == "message") { if (ZoteroPane.itemPane.mode == "message") {
document.getElementById("item-tree-main-default").focus(); document.getElementById("item-tree-main-default").focus();
return;
} }
else {
let selected = ZoteroPane.getSelectedItems(); let selected = ZoteroPane.getSelectedItems();
// If the selected collection row is duplicates, just focus on the // If the selected collection row is duplicates, just focus on the
// itemTree until the merge pane is keyboard accessible // itemTree until the merge pane is keyboard accessible
@ -776,9 +782,8 @@ var Zotero_Tabs = new function () {
// For regular items, focus the last field // 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 // We do that by moving focus backwards from the element following the pane, because Services.focus doesn't
// support MOVEFOCUS_LAST on subtrees // support MOVEFOCUS_LAST on subtrees
Services.focus.moveFocus(window, document.getElementById('zotero-context-splitter'), Services.focus.moveFocus(window, document.getElementById("zotero-context-splitter"),
Services.focus.MOVEFOCUS_BACKWARD, 0); Services.focus.MOVEFOCUS_BACKWARD, 0);
}
}; };
/** /**

View file

@ -39,6 +39,7 @@ var ZoteroPane = new function()
this.__defineGetter__('loaded', function () { return _loaded; }); this.__defineGetter__('loaded', function () { return _loaded; });
var _lastSelectedItems = []; var _lastSelectedItems = [];
var lastFocusedElement = null; var lastFocusedElement = null;
this.lastKeyPress = null;
//Privileged methods //Privileged methods
this.destroy = destroy; this.destroy = destroy;
@ -879,6 +880,7 @@ var ZoteroPane = new function()
* E.g. tab navigation hotkeys should work regardless of which component is focused. * E.g. tab navigation hotkeys should work regardless of which component is focused.
*/ */
function captureKeyDown(event) { function captureKeyDown(event) {
ZoteroPane.lastKeyPress = (event.shiftKey ? "Shift" : "") + event.key;
const cmdOrCtrlOnly = Zotero.isMac const cmdOrCtrlOnly = Zotero.isMac
? (event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey) ? (event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey)
: (event.ctrlKey && !event.shiftKey && !event.altKey); : (event.ctrlKey && !event.shiftKey && !event.altKey);
@ -1102,6 +1104,29 @@ var ZoteroPane = new function()
} }
this.handleBlur = (event) => { 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 // 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 // the last focused element to be able to return focus to it when the panel closes
if (!event.target.closest("panel")) { if (!event.target.closest("panel")) {