From 61d3c5c85ccf6eb052b02c841997bffeb5a922fa Mon Sep 17 00:00:00 2001 From: abaevbog Date: Mon, 19 Feb 2024 04:03:00 -0500 Subject: [PATCH] revision of focus management through the reader (#3687) - brought back escape behavior that just focuses the reader whenever a reader tab is opened, wherever it comes from (unless it's from an element that is .open - like an item type menu, in which case just let it get closed) - removed moving focus from contextPane title to sync button on shift-tab - using new reader methods onIframeTab, onToolbarShiftTab to move focus to the contextPane on tab from reader iframe or to the sync button on shift-tab from the toolbar So now, the focus path is: tab bar -> tabs menu -> sync button -> reader toolbar -> reader sidebar -> reader scrollable area -> contextPane title (if visible) or tab bar --- chrome/content/zotero/itemPane.js | 9 -------- chrome/content/zotero/xpcom/reader.js | 15 ++++++++++++- chrome/content/zotero/zoteroPane.js | 32 +++++++++++++++++---------- reader | 2 +- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js index de35e936c9..6422e5603b 100644 --- a/chrome/content/zotero/itemPane.js +++ b/chrome/content/zotero/itemPane.js @@ -181,15 +181,6 @@ var ZoteroItemPane = new function() { isLibraryTab ? 'zotero-view-item-sidenav' : 'zotero-context-pane-sidenav' ); - // Shift-tab from title when reader is opened focuses the last button in tabs toolbar - if (event.target.closest(".title") && event.key == "Tab" - && event.shiftKey && Zotero_Tabs.selectedType == "reader") { - let focusable = [...document.querySelectorAll("#zotero-tabs-toolbar toolbarbutton:not([disabled]):not([hidden])")]; - let btn = focusable[focusable.length - 1]; - btn.focus(); - stopEvent(); - return; - } // Tab from the scrollable area focuses the pinned pane if it exists if (event.target.classList.contains("zotero-view-item") && event.key == "Tab" && !event.shiftKey && sidenav.pinnedPane) { let pane = sidenav.getPane(sidenav.pinnedPane); diff --git a/chrome/content/zotero/xpcom/reader.js b/chrome/content/zotero/xpcom/reader.js index e09b2d55ca..acdecf7297 100644 --- a/chrome/content/zotero/xpcom/reader.js +++ b/chrome/content/zotero/xpcom/reader.js @@ -502,7 +502,20 @@ class ReaderInstance { Zotero.debug('toggle context pane') let win = Zotero.getMainWindow(); win.ZoteroContextPane.togglePane(); - + }, + onToolbarShiftTab: () => { + // Shift-tab from the toolbar focuses the sync button + let win = Zotero.getMainWindow(); + win.document.getElementById("zotero-tb-sync").focus(); + }, + onIframeTab: () => { + // Tab after the last tabstop will focus the contextPane + let win = Zotero.getMainWindow(); + let focused = win.ZoteroContextPane.focus(); + // If context pane wasn't focused (e.g. it's collapsed), focus the tab bar + if (!focused) { + win.Zotero_Tabs.moveFocus("current"); + } } }, this._iframeWindow, { cloneFunctions: true })); diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index f2c7756938..c1face74c0 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -245,7 +245,12 @@ var ZoteroPane = new function() ArrowLeft: () => null, Tab: () => { if (Zotero_Tabs.selectedIndex > 0) { - ZoteroContextPane.focus(); + let reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID); + if (reader) { + // Move focus to the reader and focus the toolbar + reader.focusFirst(); + reader.focusToolbar(); + } return null; } if (collectionsPane.getAttribute("collapsed")) { @@ -317,9 +322,6 @@ var ZoteroPane = new function() ArrowLeft: () => null, Tab: () => document.getElementById('zotero-tb-collections-search').click(), ShiftTab: () => document.getElementById('zotero-tb-sync') - // Enter: () => { - // document.getElementById('zotero-tb-collection-add').click(); - // } }, 'zotero-collections-search': { Tab: () => document.getElementById('zotero-tb-add'), @@ -857,16 +859,22 @@ var ZoteroPane = new function() if (Zotero_Tabs.selectedIndex > 0) { if (event.key === 'Escape') { // If focus is on an opened popup, let Escape just close it - // Also, do nothing if focus is inside of tabs toolbar - if (document.activeElement.open - || document.querySelector("#zotero-tabs-toolbar").contains(document.activeElement)) { + if (document.activeElement.open) { return; } - // Escape when a reader tab is opened re-focuses the tab in the tab bar - // Timeout to let the focused editable-text reset the value - setTimeout(() => { - Zotero_Tabs.moveFocus("current"); - }); + + if (!document.activeElement.classList.contains('reader')) { + let reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID); + if (reader) { + reader.focus(); + // Keep propagating if current focus is on input or textarea + // The Escape event needs to be handled by itemBox, tagBox, etc. to undo edits. + if (!["input", "textarea"].includes(document.activeElement.tagName)) { + event.preventDefault(); + event.stopPropagation(); + } + } + } } else if (event.key === 'Tab' && event.shiftKey) { let node = document.activeElement; diff --git a/reader b/reader index 39e92a143d..11001e86a9 160000 --- a/reader +++ b/reader @@ -1 +1 @@ -Subproject commit 39e92a143d23ff874789ccf360c4633939cc2784 +Subproject commit 11001e86a967583bdd7aaa0564b5dd6e5cf202df