diff --git a/chrome/content/zotero/tabs.js b/chrome/content/zotero/tabs.js index 2868aff7fd..2a5df3c917 100644 --- a/chrome/content/zotero/tabs.js +++ b/chrome/content/zotero/tabs.js @@ -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); }; /** diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 36513fa796..c859606f3d 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -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")) {