Skip to content

Commit

Permalink
context notes list focus handling followup
Browse files Browse the repository at this point in the history
- remember the last focused note-row in the contextPane
and, if possible, refocus it on shift-tab from sidenav, on
tab from the reader into notes pane, or on tab into the
notes list from the collapsible-section. Otherwise, focus the search input.
Per: zotero#4837 (comment)

- when a note is opened in the context pane, let shift-tab
from sidenave place focus on the links-box. That way,
it is not skipped during tab navigation.

- fix focus not entering note-editor on Tab from the "Go back"
button and instead wrapping around to the current tab
  • Loading branch information
abaevbog committed Nov 15, 2024
1 parent dc47650 commit dff1571
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 18 deletions.
25 changes: 25 additions & 0 deletions chrome/content/zotero/elements/contextNotesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
_numVisible = 0;

_hasParent = false;

_lastFocusedNote = null;

get notes() {
return [...this._itemNotes, ...this._allNotes];
Expand Down Expand Up @@ -118,6 +120,7 @@
this.addEventListener('click', this._handleClick);
this.addEventListener('contextmenu', this._handleContextMenu);
this.addEventListener('keydown', this._handleKeyDown);
this.addEventListener("focusin", this._handleFocusIn);
this.render();
}

Expand Down Expand Up @@ -152,6 +155,14 @@
}
}

refocusLastFocusedNote() {
if (this._lastFocusedNote) {
this._lastFocusedNote.focus();
return document.activeElement == this._lastFocusedNote;
}
return false;
}

_makeRow(note) {
let row = document.createXULElement('note-row');
row.note = note;
Expand Down Expand Up @@ -184,9 +195,23 @@
}
};

// Remember which note row was last focused to be able to return focus to it
_handleFocusIn = (event) => {
if (event.target.tagName !== "note-row" && !event.target.classList.contains("more")) return;
this._lastFocusedNote = event.target;
};

// ArrowUp/Down navigation between notes
// Tab from a note-row focuses sidenav, Shift-Tab from a note focuses the section header
// Tab from twisty icon into the notes list will try to refocus the last focused note
_handleKeyDown = (event) => {
if (event.key == "Tab" && event.target.classList.contains("twisty")) {
let section = event.target.closest("collapsible-section");
if (this._lastFocusedNote && section.contains(this._lastFocusedNote)) {
this.refocusLastFocusedNote();
event.preventDefault();
}
}
if (event.target.tagName !== "note-row" && !event.target.classList.contains("more")) return;
if (event.key == "ArrowDown") {
event.target.nextElementSibling?.focus();
Expand Down
4 changes: 1 addition & 3 deletions chrome/content/zotero/elements/contextPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,7 @@
return true;
}
else if (this.mode == "notes") {
// Tab into the notes pane
Services.focus.moveFocus(window, this._getCurrentNotesContext(), Services.focus.MOVEFOCUS_FORWARD, 0);
return true;
return this._getCurrentNotesContext().focus();
}
}
return false;
Expand Down
16 changes: 7 additions & 9 deletions chrome/content/zotero/elements/itemPaneSidenav.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,19 +388,17 @@
handleKeyDown = (event) => {
if (event.key == "Tab" && !event.shiftKey) {
// Wrap focus around to the tab bar
Services.focus.moveFocus(window, document.getElementById("zotero-title-bar"), Services.focus.MOVEFOCUS_FORWARD, 0);
Zotero_Tabs.moveFocus("current");
event.preventDefault();
}
if (event.key == "Tab" && event.shiftKey) {
if (this._contextNotesPaneVisible) {
// Tab into notes pane to focus search bar
Services.focus.moveFocus(window, this._contextNotesPane, Services.focus.MOVEFOCUS_FORWARD, 0);
}
else {
// Shift-Tab out of sidenav to itemPane
Services.focus.moveFocus(window, this, Services.focus.MOVEFOCUS_BACKWARD, 0);
}
event.preventDefault();
if (this._contextNotesPaneVisible && this._contextNotesPane.selectedPanel.mode == "notesList") {
let focusHandled = this._contextNotesPane.selectedPanel.focus();
if (focusHandled) return;
}
// Shift-Tab out of sidenav to itemPane
Services.focus.moveFocus(window, this, Services.focus.MOVEFOCUS_BACKWARD, 0);
}
if (["ArrowUp", "ArrowDown"].includes(event.key)) {
// Up/Down arrow navigation
Expand Down
5 changes: 4 additions & 1 deletion chrome/content/zotero/elements/notesContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@

focus() {
if (this.mode == "notesList") {
this.input.focus();
let refocused = this.notesList.refocusLastFocusedNote();
if (!refocused) {
this.input.focus();
}
return true;
}
else {
Expand Down
7 changes: 2 additions & 5 deletions chrome/content/zotero/zoteroPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -1138,14 +1138,11 @@ var ZoteroPane = new function()
// Shift-tab from the header into the itemsView.
if (Services.focus.activeWindow === window && this.lastKeyPress === "Tab"
&& (itemPaneLostFocus || contextPaneLostFocus)) {
// event.relatedTarget is null when moving focus in or out of <iframe> or <browser>
// so make sure to not refocus tabs when focusing inside of note-editor or reader
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
Expand Down

0 comments on commit dff1571

Please sign in to comment.