vpat 17: itemTree view menu for non-main windows (#4433)

- adds View > Columns and View > Sort By menu options
  to all windows that contains an itemTree (Select Items dialog,
  Advanced Search, classic Add Citation, Edit Bibliography
  dialog).
- the menubar is global on macOS. On Windows/Linux it is
  displayed and focused on Alt keypress
- added menu option to move a selected column left,
  which we need as an alternative for drag-drop reordering
  that would not require using a mouse
This commit is contained in:
abaevbog 2024-10-22 23:25:38 -07:00 committed by GitHub
parent 504ec7eb4a
commit 8e87aa15e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 203 additions and 37 deletions

View file

@ -29,6 +29,7 @@ Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
Services.scriptloader.loadSubScript("chrome://global/content/customElements.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/base.js", this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemPaneSection.js', this);
Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemTreeMenuBar.js', this);
{
// https://searchfox.org/mozilla-central/rev/8e885f04a0a4ff6d64ea59741c10d9b8e45d9ff8/toolkit/content/customElements.js#826-832

View file

@ -0,0 +1,150 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2022 Corporation for Digital Scholarship
Vienna, Virginia, USA
https://www.zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
class ItemTreeMenuBar extends XULElement {
// Menu bar containing View options for manipulating the itemTree table
// (View > Columns, Sort By, Move Column).
// Added to windows without a menubar by ItemTree.init()
// to expose the functionality of table manipulation to keyboard users.
// On Windows or Linux, this menubar appears on Alt keypress.
constructor() {
super();
this._inactiveTimeout = null;
this.content = MozXULElement.parseXULToFragment(`
<keyset id="sortSubmenuKeys">
<key id="key_sortCol0"/>
<key id="key_sortCol1"/>
<key id="key_sortCol2"/>
<key id="key_sortCol3"/>
<key id="key_sortCol4"/>
<key id="key_sortCol5"/>
<key id="key_sortCol6"/>
<key id="key_sortCol7"/>
<key id="key_sortCol8"/>
<key id="key_sortCol9"/>
</keyset>
<menubar id="main-menubar">
<menu id="view-menu"
label="&viewMenu.label;"
accesskey="&viewMenu.accesskey;">
<menupopup id="menu_viewPopup">
<menu id="column-picker-submenu"
class="menu-type-library"
label="&columns.label;">
<menupopup/>
</menu>
<menu id="sort-submenu"
class="menu-type-library"
label="&sortBy.label;">
<menupopup/>
</menu>
</menupopup>
</menu>
</menubar>
`, ['chrome://zotero/locale/standalone.dtd']);
}
connectedCallback() {
this.append(document.importNode(this.content, true));
this.hidden = true;
}
// Show View > Columns, Sort By menus for windows that have an itemTree
static handleItemTreeMenuShowing(event, menu, itemsView) {
if (event.target !== menu.menupopup) {
return;
}
menu.menupopup.replaceChildren();
if (menu.id == "column-picker-submenu") {
// View > Columns
itemsView.buildColumnPickerMenu(menu.menupopup);
}
else if (menu.id == "sort-submenu") {
// View > Sort By
itemsView.buildSortMenu(menu.menupopup);
for (let i = 0; i < 10; i++) {
if (!menu.menupopup.children[i]) {
break;
}
menu.menupopup.children[i].setAttribute('key', 'key_sortCol' + i);
}
}
}
// Set the access keys for menuitems to sort the itemTree
static setItemTreeSortKeys(itemsView) {
let sortSubmenuKeys = document.getElementById('sortSubmenuKeys');
for (let i = 0; i < 10; i++) {
let key = sortSubmenuKeys.children[i];
key.setAttribute('modifiers', Zotero.isMac ? 'accel alt control' : 'alt');
key.setAttribute('key', (i + 1) % 10);
key.addEventListener('command', () => {
if (!window.Zotero_Tabs || window.Zotero_Tabs.selectedType == 'library') {
itemsView.toggleSort(i, true);
}
});
}
}
init(itemTree) {
this.constructor.setItemTreeSortKeys(itemTree);
for (let menu of [...this.querySelectorAll(".menu-type-library")]) {
menu.addEventListener("popupshowing", (event) => {
this.constructor.handleItemTreeMenuShowing(event, menu, itemTree);
});
}
if (!Zotero.isMac) {
// On Windows and Linux, display and focus menubar on Alt keypress
document.addEventListener("keydown", (event) => {
if (event.key == "Alt") {
this.hidden = !this.hidden;
document.getElementById("main-menubar").focus();
}
}, true);
// Hide menubar on click or tab away. If a selected menu is clicked, it will
// fire DOMMenuBarInactive event first followed by DOMMenuBarActive.
// Listen to both events and hide inactive menu after delay if it is not cancelled.
// https://searchfox.org/mozilla-central/source/browser/base/content/browser-customization.js#165
document.addEventListener("DOMMenuBarInactive", (_) => {
this._inactiveTimeout = setTimeout(() => {
this._inactiveTimeout = null;
this.hidden = true;
});
});
document.addEventListener("DOMMenuBarActive", (_) => {
if (this._inactiveTimeout) {
clearTimeout(this._inactiveTimeout);
this._inactiveTimeout = null;
}
this.hidden = false;
});
}
}
}
customElements.define("item-tree-menu-bar", ItemTreeMenuBar);

View file

@ -59,6 +59,12 @@ var ItemTree = class ItemTree extends LibraryTree {
Zotero.debug(`Initializing React ItemTree ${opts.id}`);
var ref;
opts.domEl = domEl;
let itemTreeMenuBar = null;
// Add a menubar with View options to manipulate the table (only if a menubar doesn't already exist in .xhtml)
if (!document.querySelector("menubar")) {
itemTreeMenuBar = document.createXULElement("item-tree-menu-bar");
document.documentElement.prepend(itemTreeMenuBar);
}
await new Promise((resolve) => {
ReactDOM.createRoot(domEl).render(<ItemTree ref={(c) => {
ref = c;
@ -66,6 +72,9 @@ var ItemTree = class ItemTree extends LibraryTree {
} } {...opts} />);
});
if (itemTreeMenuBar) {
itemTreeMenuBar.init(ref);
}
Zotero.debug(`React ItemTree ${opts.id} initialized`);
return ref;
}
@ -2666,6 +2675,41 @@ var ItemTree = class ItemTree extends LibraryTree {
// sep.setAttribute('anonid', prefix + 'sep');
menupopup.appendChild(sep);
//
// Move Column Left
//
let moveColumnMenu = document.createXULElement('menu');
document.l10n.setAttributes(
moveColumnMenu,
Zotero.rtl ? 'menu-view-columns-move-right' : 'menu-view-columns-move-left'
);
moveColumnMenu.setAttribute('anonid', prefix + 'move-column');
let moveColumnPopup = document.createXULElement('menupopup');
moveColumnPopup.setAttribute('anonid', prefix + 'move-column-popup');
moveColumnMenu.appendChild(moveColumnPopup);
menupopup.appendChild(moveColumnMenu);
let firstColumn = true;
// Only list visible columns
for (let i = 0; i < columns.length; i++) {
let column = columns[i];
if (column.hidden) continue;
// Skip first column (since there is nowhere to move it)
if (firstColumn) {
firstColumn = false;
continue;
}
let label = formatColumnName(column);
let menuitem = document.createXULElement('menuitem');
menuitem.setAttribute('label', label);
menuitem.setAttribute('colindex', i);
// Swap the column with its previous visible neighbor
menuitem.addEventListener('command', () => {
let previousIndex = columns.findLastIndex((col, index) => index < i && !col.hidden);
this.tree._columns.setOrder(i, previousIndex);
});
moveColumnPopup.appendChild(menuitem);
}
//
// Restore Default Column Order
//

View file

@ -1655,17 +1655,7 @@ var ZoteroPane = new function()
ZoteroPane.itemsView.onRefresh.addListener(() => ZoteroPane.setTagScope());
ZoteroPane.itemsView.waitForLoad().then(() => Zotero.uiIsReady());
let sortSubmenuKeys = document.getElementById('sortSubmenuKeys');
for (let i = 0; i < 10; i++) {
let key = sortSubmenuKeys.children[i];
key.setAttribute('modifiers', Zotero.isMac ? 'accel alt control' : 'alt');
key.setAttribute('key', (i + 1) % 10);
key.addEventListener('command', () => {
if (Zotero_Tabs.selectedType === 'library') {
ZoteroPane.itemsView.toggleSort(i, true);
}
});
}
ItemTreeMenuBar.setItemTreeSortKeys(ZoteroPane.itemsView);
}
catch (e) {
Zotero.logError(e);
@ -6745,30 +6735,6 @@ var ZoteroPane = new function()
this.itemPane.handleResize();
}
this.onColumnPickerPopupShowing = function (event) {
let menuPopup = document.getElementById('column-picker-submenu').menupopup;
if (event.target !== menuPopup) {
return;
}
menuPopup.replaceChildren();
this.itemsView?.buildColumnPickerMenu(menuPopup);
};
this.onSortPopupShowing = function (event) {
let menuPopup = document.getElementById('sort-submenu').menupopup;
if (event.target !== menuPopup) {
return;
}
menuPopup.replaceChildren();
this.itemsView?.buildSortMenu(menuPopup);
for (let i = 0; i < 10; i++) {
if (!menuPopup.children[i]) {
break;
}
menuPopup.children[i].setAttribute('key', 'key_sortCol' + i);
}
};
// Set the label of the dynamic tooltip. Can be used when we cannot set .tooltiptext
// property, e.g. if we don't want the tooltip to be announced by screenreaders.

View file

@ -645,13 +645,13 @@
<menu id="column-picker-submenu"
class="menu-type-library"
label="&columns.label;"
onpopupshowing="ZoteroPane_Local.onColumnPickerPopupShowing(event)">
onpopupshowing="ItemTreeMenuBar.handleItemTreeMenuShowing(event, this, ZoteroPane.itemsView)">
<menupopup/>
</menu>
<menu id="sort-submenu"
class="menu-type-library"
label="&sortBy.label;"
onpopupshowing="ZoteroPane_Local.onSortPopupShowing(event)">
onpopupshowing="ItemTreeMenuBar.handleItemTreeMenuShowing(event, this, ZoteroPane.itemsView)">
<menupopup/>
</menu>
<menuseparator class="menu-type-library"/>

View file

@ -75,6 +75,11 @@ menu-deletePermanently =
menu-tools-plugins =
.label = Plugins
menu-view-columns-move-left =
.label = Move Column Left
menu-view-columns-move-right =
.label = Move Column Right
main-window-command =
.label = { -app-name }