Unify tab bar on Windows/Linux

This commit is contained in:
windingwind 2023-11-15 17:07:54 +08:00 committed by Dan Stillman
parent 464c37e66f
commit 7da00957ef
21 changed files with 1077 additions and 129 deletions

View file

@ -46,12 +46,139 @@ const ZoteroStandalone = new function() {
window.document.documentElement.setAttribute('sizemode', 'normal');
}
// Create tab bar by default
document.documentElement.setAttribute('drawintitlebar', true);
document.documentElement.setAttribute('tabsintitlebar', true);
if (Zotero.isMac) {
// Create tab bar by default
document.documentElement.setAttribute('drawintitlebar', true);
document.documentElement.setAttribute('tabsintitlebar', true);
document.documentElement.setAttribute('chromemargin', '0,-1,-1,-1');
}
else {
document.documentElement.setAttribute('chromemargin', '0,2,2,2');
}
if (Zotero.isWin) {
let windowIcon = document.querySelector(".titlebar-icon");
// Simulate Windows window control
windowIcon.addEventListener("dblclick", (ev) => {
if (ev.button !== 0) {
return;
}
window.close();
});
const DBLCLICK_INTERVAL = 300;
let leftClicked = false;
let simulatingClick = false;
windowIcon.addEventListener("click", (ev) => {
// If already/about to open, do nothing.
if (simulatingClick || leftClicked) {
return;
}
// Left-click: open at icon
if (ev.button === 0) {
leftClicked = true;
// Reset leftClicked flag to allow open again
const onWindowClick = (ev) => {
leftClicked = false;
window.removeEventListener("click", onWindowClick);
}
// Delay to allow dblclick happen
setTimeout(() => {
// Clicking inside image (36*36)
openWindowMenu(2, 35, ()=>{
setTimeout(()=>window.addEventListener("click", onWindowClick), 0);
});
}, DBLCLICK_INTERVAL);
return;
}
// Right-click: open at cursor
if (ev.button === 2) {
openWindowMenu();
}
/**
* What we do here:
* We want to open the window menu when clicking the window icon.
* Why we do this way:
* The window menu is a native menu, which is not accessible from JS.
* Firefox didn't expose this to the JS level (they do at a C++ level),
* which forces us to simulate a native right-click on a `-moz-window-drag: drag` element.
* How we do this:
* 1. temporarily change the -moz-window-drag of the icon after clicking,
* 2. simulate a native right-click (which triggers the window menu),
* 3. change the -moz-window-drag back to no-drag.
* The function is to open window menu. If X or Y not given, use click position.
*/
function openWindowMenu(clientX = undefined, clientY = undefined, callback = undefined) {
simulatingClick = true;
windowIcon.style["-moz-window-dragging"] = "drag";
const resolution = windowUtils.getResolution();
const scaleValue = window.devicePixelRatio;
const getX = (inputX) => {
let winInnerOffsetX
= window.top.mozInnerScreenX
+ (window.mozInnerScreenX - window.top.mozInnerScreenX) * resolution;
return (
(inputX * resolution + winInnerOffsetX)* scaleValue
);
};
const getY = (inputY) => {
let winInnerOffsetY
= window.top.mozInnerScreenY
+ (window.mozInnerScreenY - window.top.mozInnerScreenY) * resolution;
return (
(inputY * resolution + winInnerOffsetY) * scaleValue
);
};
const x = getX(clientX ?? ev.x);
const y = getY(clientY ?? ev.y);
// Following implementation from https://searchfox.org/mozilla-central/rev/ffdc4971dc18e1141cb2a90c2b0b776365650270/testing/mochitest/tests/SimpleTest/EventUtils.js#1323
windowUtils.sendNativeMouseEvent(
x,
y,
windowUtils.NATIVE_MOUSE_MESSAGE_BUTTON_DOWN,
2, // button
0, // modifierFlags
windowIcon,
function () {
windowUtils.sendNativeMouseEvent(
x,
y,
windowUtils.NATIVE_MOUSE_MESSAGE_BUTTON_UP,
2, // button
0, // modifierFlags
windowIcon,
{
observe: (_subject, topic, _data) => {
if (topic == "mouseevent") {
if (typeof clientX !== "undefined" || typeof clientY !== "undefined")
{
// Move mouse back if the click position is given
windowUtils.sendNativeMouseEvent(
getX(ev.x),
getY(ev.y),
windowUtils.NATIVE_MOUSE_MESSAGE_MOVE,
0, // button
0, // modifierFlags
windowIcon
);
}
windowIcon.style["-moz-window-dragging"] = "no-drag";
callback && callback();
simulatingClick = false;
}
},
},
);
}
);
}
});
}
this.switchMenuType('library');
this._notifierID = Zotero.Notifier.registerObserver(

View file

@ -96,7 +96,6 @@ var Zotero_Tabs = new function () {
return;
}
document.title = (tab.title.length ? tab.title + ' - ' : '') + Zotero.appName;
this._updateTabBar();
// Hide any tab `title` tooltips that might be open
window.Zotero_Tooltip.stop();
};
@ -698,48 +697,6 @@ var Zotero_Tabs = new function () {
popup.openPopupAtScreen(x, y, true);
};
/**
* Update state of the tab bar.
* Only used on Windows and Linux. On macOS, the tab bar is always shown.
*/
this._updateTabBar = function () {
if (Zotero.isMac) {
return;
}
if (this._tabs.length == 1) {
this._hideTabBar();
}
else {
this._showTabBar();
}
};
/**
* Show the tab bar.
* Only used on Windows and Linux. On macOS, the tab bar is always shown.
*/
this._showTabBar = function () {
if (Zotero.isMac) {
return;
}
document.getElementById('titlebar').hidden = false;
document.getElementById('tab-bar-container').hidden = false;
document.getElementById('main-window').removeAttribute('legacytoolbar');
};
/**
* Hide the tab bar.
* Only used on Windows and Linux. On macOS, the tab bar is always shown.
*/
this._hideTabBar = function () {
if (Zotero.isMac) {
return;
}
document.getElementById('titlebar').hidden = true;
document.getElementById('tab-bar-container').hidden = true;
document.getElementById('main-window').setAttribute('legacytoolbar', 'true');
};
// Used to move focus back to itemTree or contextPane from the tabs.
this.focusWrapAround = function () {
// If no item is selected, focus items list.

View file

@ -131,8 +131,8 @@
<key id="key_back"/>
<key id="key_forward"/>
</keyset>
<keyset id="editMenuKeys">
<keyset id="editMenuKeys">
<key id="key_undo" data-l10n-id="text-action-undo-shortcut" modifiers="accel" command="cmd_undo"/>
<!-- l10n and modifiers set in platformKeys.js -->
<key id="key_redo" command="cmd_redo"/>
@ -150,11 +150,32 @@
</keyset>
<vbox id="titlebar">
<hbox id="titlebar-buttonbox-container" skipintoolbarset="true">
<hbox id="titlebar-buttonbox">
<toolbarbutton class="titlebar-button titlebar-min" oncommand="window.minimize();"/>
<toolbarbutton class="titlebar-button titlebar-max" oncommand="onTitlebarMaxClick();"/>
<toolbarbutton class="titlebar-button titlebar-close" command="cmd_closeWindow"/>
<hbox class="titlebar-icon-container">
<image class="titlebar-icon" src="chrome://zotero/skin/zotero-new-z-16px.png"></image>
</hbox>
<hbox class="titlebar-buttonbox-container" skipintoolbarset="true">
<hbox class="titlebar-buttonbox titlebar-color">
<!-- TODO: remove `titlebar-btn` when we are on FX115. See https://phabricator.services.mozilla.com/D162757 -->
<toolbarbutton class="titlebar-button titlebar-min"
titlebar-btn="min"
oncommand="window.minimize();"
data-l10n-id="browser-window-minimize-button"
/>
<toolbarbutton class="titlebar-button titlebar-max"
titlebar-btn="max"
oncommand="window.maximize();"
data-l10n-id="browser-window-maximize-button"
/>
<toolbarbutton class="titlebar-button titlebar-restore"
titlebar-btn="max"
oncommand="window.fullScreen ? BrowserFullScreen() : window.restore();"
data-l10n-id="browser-window-restore-down-button"
/>
<toolbarbutton class="titlebar-button titlebar-close"
titlebar-btn="close"
oncommand="window.close();"
data-l10n-id="browser-window-close-button"
/>
</hbox>
</hbox>
</vbox>
@ -162,7 +183,7 @@
<!-- Menu -->
<toolbar type="menubar" id="toolbar-menubar" class="chromeclass-menubar" customizable="true"
defaultset="menubar-items"
mode="icons" iconsize="small" defaulticonsize="small"
mode="icons" iconsize="small" defaulticonsize="small" autohide="false"
context="toolbar-context-menu">
<toolbaritem id="menubar-items" align="center">
<!-- TODO: Localize labels -->
@ -461,12 +482,27 @@
<menuitem
id="view-menuitem-standard"
label="&standardView.label;"
type="checkbox"
name="menu-ui-layout"
type="radio"
/>
<menuitem
id="view-menuitem-stacked"
label="&stackedView.label;"
type="checkbox"
name="menu-ui-layout"
type="radio"
/>
<menuseparator/>
<menuitem
id="view-menuitem-ui-density-compact"
data-l10n-id="menu-ui-density-compact"
name="menu-ui-density"
type="radio"
/>
<menuitem
id="view-menuitem-ui-density-comfortable"
data-l10n-id="menu-ui-density-comfortable"
name="menu-ui-density"
type="radio"
/>
<menuseparator/>
<menuitem
@ -486,20 +522,6 @@
/>
</menupopup>
</menu>
<menu id="density-menu" data-l10n-id="menu-ui-density">
<menupopup oncommand="ZoteroStandalone.onViewMenuItemClick(event)">
<menuitem
id="view-menuitem-ui-density-compact"
data-l10n-id="menu-ui-density-compact"
type="checkbox"
/>
<menuitem
id="view-menuitem-ui-density-comfortable"
data-l10n-id="menu-ui-density-comfortable"
type="checkbox"
/>
</menupopup>
</menu>
<menu id="font-size-menu"
label="&fontSize.label;">
<menupopup oncommand="ZoteroStandalone.onViewMenuItemClick(event)">

View file

@ -0,0 +1,3 @@
<svg width="4" height="4" viewBox="0 0 4 4" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="2" cy="2" r="2" fill="context-fill" fill-opacity="context-fill-opacity"/>
</svg>

After

Width:  |  Height:  |  Size: 185 B

300
package-lock.json generated
View file

@ -47,7 +47,7 @@
"multimatch": "^2.1.0",
"pako": "^2.1.0",
"react-virtualized": "^9.22.4",
"sass": "^1.28.0",
"sass": "^1.69.5",
"sinon": "^7.3.2",
"universalify": "^0.1.1"
}
@ -3961,6 +3961,12 @@
"node": ">= 4"
}
},
"node_modules/immutable": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
"integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
"dev": true
},
"node_modules/import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
@ -5481,7 +5487,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"dev": true,
"optional": true,
"engines": {
"node": ">=8.6"
}
@ -5953,18 +5958,164 @@
"dev": true
},
"node_modules/sass": {
"version": "1.28.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.28.0.tgz",
"integrity": "sha512-9FWX/0wuE1KxwfiP02chZhHaPzu6adpx9+wGch7WMOuHy5npOo0UapRI3FNSHva2CczaYJu2yNUBN8cCSqHz/A==",
"version": "1.69.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz",
"integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==",
"dev": true,
"dependencies": {
"chokidar": ">=2.0.0 <4.0.0"
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=8.9.0"
"node": ">=14.0.0"
}
},
"node_modules/sass/node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/sass/node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/sass/node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/sass/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/sass/node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/sass/node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/scheduler": {
@ -6273,6 +6424,15 @@
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-resolve": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
@ -10119,6 +10279,12 @@
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
"immutable": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
"integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
"dev": true
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
@ -11364,8 +11530,7 @@
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"dev": true,
"optional": true
"dev": true
},
"pify": {
"version": "4.0.1",
@ -11759,12 +11924,115 @@
"dev": true
},
"sass": {
"version": "1.28.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.28.0.tgz",
"integrity": "sha512-9FWX/0wuE1KxwfiP02chZhHaPzu6adpx9+wGch7WMOuHy5npOo0UapRI3FNSHva2CczaYJu2yNUBN8cCSqHz/A==",
"version": "1.69.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz",
"integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"dependencies": {
"anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
}
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"optional": true
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"requires": {
"binary-extensions": "^2.0.0"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"requires": {
"picomatch": "^2.2.1"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"scheduler": {
@ -12023,6 +12291,12 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true
},
"source-map-resolve": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",

View file

@ -56,7 +56,7 @@
"multimatch": "^2.1.0",
"pako": "^2.1.0",
"react-virtualized": "^9.22.4",
"sass": "^1.28.0",
"sass": "^1.69.5",
"sinon": "^7.3.2",
"universalify": "^0.1.1"
}

View file

@ -34,6 +34,7 @@
@import "components/item-tree";
@import "components/longTagFixer";
@import "components/mainWindow";
@import "components/menu";
@import "components/notesList";
@import "components/progressMeter";
@import "components/publications-dialog";

View file

@ -7,7 +7,7 @@
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1)+$replace +str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
@return str-slice($string, 1, $index - 1)+$replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
@return $string;

View file

@ -36,28 +36,38 @@
// underneath selectors listed in arguments, e.g., .virtualized-table .row
// by default. See `state` mixin for more details.
@mixin focus-states(
$selectedState: '.row.selected',
$focused: '.virtualized-table:focus-within'
) {
@media (prefers-color-scheme: light) {
@content("light");
@include state($selectedState) {
@include state($focused) {
@content("white");
}
}
}
$selectedState: '.row.selected',
$focused: '.virtualized-table:focus-within'
) {
@media (prefers-color-scheme: light) {
@content("light");
@include state($selectedState) {
@include state($focused) {
@content("white");
}
}
}
@media (prefers-color-scheme: dark) {
@content("dark");
@include state($selectedState) {
@include state($focused) {
@content("white");
}
}
}
@media (prefers-color-scheme: dark) {
@content("dark");
@include state($selectedState) {
@include state($focused) {
@content("white");
}
}
}
}
// An implementation of Firefox light-dark() CSS mixin, which is not supported in 102
@mixin light-dark($prop, $light-color, $dark-color) {
@media (prefers-color-scheme: light) {
#{$prop}: $light-color;
}
@media (prefers-color-scheme: dark) {
#{$prop}: $dark-color;
}
}
@mixin color-scheme {

View file

@ -8,6 +8,10 @@
-make-icon-background($icon, 'dark', $size, $prefix, $has2x) center/0;
}
@mixin svgicon-menu($icon, $color, $size: "16", $prefix: false, $has2x: false) {
list-style-image: -make-icon-background($icon, $color, $size, $prefix, $has2x),
}
@function -make-icon-background($icon, $color, $size, $prefix, $has2x) {
@if $has2x {
@if $prefix {

View file

@ -1,7 +1,12 @@
// Styling for displaying tabs in the title bar
:root:not([legacytoolbar="true"]) {
& {
--tab-min-height: 30px;
@media (-moz-platform: windows) {
--tab-min-height: 36px;
}
@media not (-moz-platform: windows) {
--tab-min-height: 30px;
}
--tabs-border-color: rgba(0,0,0,.3);
--tabline-color: #0a84ff;
@ -61,7 +66,7 @@
#titlebar {
margin-bottom: -30px;
margin-bottom: calc(0px - var(--tab-min-height));
-moz-box-pack: center;
}
@ -98,31 +103,39 @@
visibility: collapse;
}
/* NB: these would be margin-inline-start/end if it wasn't for the fact that OS X
* doesn't reverse the order of the items in the titlebar in RTL mode. */
.titlebar-placeholder[type="caption-buttons"],
#titlebar-buttonbox {
margin-right: 12px;
margin-left: 12px;
@media (-moz-platform: macos) {
/* NB: these would be margin-inline-start/end if it wasn't for the fact that OS X
* doesn't reverse the order of the items in the titlebar in RTL mode. */
.titlebar-placeholder[type="caption-buttons"],
.titlebar-buttonbox {
margin-right: 12px;
margin-left: 12px;
}
.titlebar-buttonbox {
-moz-appearance: -moz-window-button-box;
}
/* Fullscreen and caption buttons don't move with RTL on OS X so override the automatic ordering. */
.titlebar-buttonbox-container:-moz-locale-dir(rtl) {
-moz-box-ordinal-group: 1000;
}
.titlebar-buttonbox-container:-moz-locale-dir(ltr) {
-moz-box-ordinal-group: 0;
}
.titlebar-icon {
display: none;
}
}
#titlebar-buttonbox {
-moz-appearance: -moz-window-button-box;
}
/* Fullscreen and caption buttons don't move with RTL on OS X so override the automatic ordering. */
#titlebar-buttonbox-container:-moz-locale-dir(rtl) {
-moz-box-ordinal-group: 1000;
}
#titlebar-buttonbox-container:-moz-locale-dir(ltr) {
-moz-box-ordinal-group: 0;
}
#tab-bar-container {
background: var(--material-tabbar);
-moz-window-dragging: drag;
}
@ -141,5 +154,21 @@
#zotero-layout-switcher .zotero-toolbar {
background: var(--material-tabbar);
border-bottom: var(--material-panedivider);
-moz-window-dragging: no-drag;
}
#zotero-tabs-toolbar {
background: var(--material-tabbar);
}
}
:root:not([sizemode=maximized], [sizemode=fullscreen]) .titlebar-restore,
:root:is([sizemode=maximized], [sizemode=fullscreen]) .titlebar-max {
display: none;
}
#navigator-toolbox {
menu {
font-size: var(--zotero-font-size);
}
}

View file

@ -0,0 +1,13 @@
$icons: (
attachments-web-link: "link",
attachments-file: "document",
attachments-link: "document-linked"
);
@each $cls, $icon in $icons {
.zotero-menuitem-#{$cls} {
@include focus-states using ($color) {
@include svgicon-menu($icon, $color, "16", "item-type");
}
}
}

View file

@ -1,5 +1,20 @@
menupopup, panel {
--panel-padding-block: 0;
--panel-border-radius: 2px;
--panel-border-color: ActiveBorder;
}
--panel-border-radius: 6px;
--panel-background: var(--material-toolbar);
--menuitem-hover-background-color: var(--fill-quinary);
--menuitem-disabled-hover-background-color: var(--fill-quinary);
@include light-dark(--panel-border-color, #dddddd, #1c1c1c);
}
menupopup {
menu {
menupopup {
margin-top: -4px;
}
}
}
:is(panel, menupopup)::part(content) {
padding: 6px 0px 6px 0px;
}

12
scss/linux/_tabBar.scss Normal file
View file

@ -0,0 +1,12 @@
#tab-bar-container {
--safe-area-start: 30px;
--safe-area-end: 0px;
}
.tabs {
-moz-window-dragging: drag;
}
.tab {
-moz-window-dragging: no-drag;
}

147
scss/linux/_titleBar.scss Normal file
View file

@ -0,0 +1,147 @@
// Prevent window background overflows window border
#main-window {
background-color: initial;
}
// Prevent background flicker
#browser {
background: var(--material-tabbar);
}
#navigator-toolbox {
height: var(--tab-min-height);
-moz-box-pack: center;
pointer-events: none;
}
#toolbar-menubar {
pointer-events: none;
visibility: visible;
// space for 30*30 icon
padding-left: var(--tab-min-height);
}
#menubar-items {
pointer-events: all;
}
#titlebar {
pointer-events: none;
}
.titlebar-buttonbox {
pointer-events: all;
}
.titlebar-icon-container {
margin-bottom: calc(0px - var(--tab-min-height));
height: var(--tab-min-height);
}
.titlebar-icon {
-moz-window-dragging: drag;
// to make the icon 30*30
margin: 7px;
object-fit: none;
}
.titlebar-buttonbox-container {
-moz-box-pack: end;
height: var(--tab-min-height);
}
// Following rules from https://searchfox.org/mozilla-central/source/browser/themes/linux/browser.css
/* The button box must appear on top of the navigator-toolbox in order for
* click and hover mouse events to work properly for the button in the restored
* window state. Otherwise, elements in the navigator-toolbox, like the menubar,
* can swallow those events. */
.titlebar-buttonbox {
z-index: 1;
align-items: center;
// Disable button stretch
-moz-box-align: baseline;
}
/* Render titlebar command buttons according to system config.
* Use full scale icons here as the Gtk+ does. */
.titlebar-min {
appearance: auto;
-moz-default-appearance: -moz-window-button-minimize;
order: env(-moz-gtk-csd-minimize-button-position);
}
.titlebar-max {
appearance: auto;
-moz-default-appearance: -moz-window-button-maximize;
order: env(-moz-gtk-csd-maximize-button-position);
}
.titlebar-restore {
appearance: auto;
-moz-default-appearance: -moz-window-button-restore;
order: env(-moz-gtk-csd-maximize-button-position);
}
.titlebar-close {
appearance: auto;
-moz-default-appearance: -moz-window-button-close;
order: env(-moz-gtk-csd-close-button-position);
}
/* When using lightweight themes, use our own buttons since native ones might
* assume a native background in order to be visible. */
.titlebar-button:-moz-lwtheme {
appearance: none;
border-radius: 100%;
}
.titlebar-button > .toolbarbutton-icon:-moz-lwtheme {
padding: 6px;
-moz-context-properties: stroke;
stroke: currentColor;
}
.titlebar-min:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/window-controls/minimize.svg);
}
.titlebar-max:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/window-controls/maximize.svg);
}
.titlebar-restore:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/window-controls/restore.svg);
}
.titlebar-close:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/window-controls/close.svg);
}
.titlebar-button:-moz-lwtheme:hover {
background-color: color-mix(in srgb, currentColor 12%, transparent);
}
.titlebar-button:-moz-lwtheme:hover:active {
background-color: color-mix(in srgb, currentColor 20%, transparent);
}
.titlebar-close:-moz-lwtheme:hover {
background-color: #d70022;
color: white;
}
.titlebar-close:-moz-lwtheme:hover:active {
background-color: #ff0039;
}
@media (not (-moz-gtk-csd-minimize-button)) {
.titlebar-min {
display: none;
}
}
@media (not (-moz-gtk-csd-maximize-button)) {
.titlebar-restore,
.titlebar-max {
display: none;
}
}
@media (not (-moz-gtk-csd-close-button)) {
.titlebar-close {
display: none;
}
}
@media (-moz-gtk-csd-reversed-placement) {
.titlebar-buttonbox-container {
order: -1;
}
}

View file

@ -10,3 +10,7 @@
.tab {
-moz-window-dragging: no-drag;
}
#navigator-toolbox {
border: 0;
}

41
scss/win/_menupopup.scss Normal file
View file

@ -0,0 +1,41 @@
menupopup, panel {
--panel-border-radius: 8px;
--panel-background: var(--material-toolbar);
--menuitem-hover-background-color: var(--fill-quinary);
--menuitem-disabled-hover-background-color: var(--fill-quinary);
@include light-dark(--panel-border-color, #dddddd, #1c1c1c);
}
menupopup {
menu, menuitem {
margin-left: 4px;
margin-right: 4px;
border-radius: 4px;
}
menu {
menupopup {
padding-left: 6px;
margin-top: -4px;
}
}
menuseparator {
margin: 4px 0px 4px 0px;
}
}
.menu-accel {
color: var(--fill-secondary);
font-size: 11px;
}
.menu-iconic-left[disabled="true"] {
opacity: 50%;
}
@media (-moz-windows-non-native-menus) {
menuitem:is([checked="true"]):is([type="radio"]) > .menu-iconic-left {
list-style-image: url("chrome://zotero/skin/win/menu-radio.svg");
width: 4px;
height: 4px;
}
}

12
scss/win/_tabBar.scss Normal file
View file

@ -0,0 +1,12 @@
#tab-bar-container {
--safe-area-start: 30px;
--safe-area-end: 0px;
}
.tabs {
-moz-window-dragging: drag;
}
.tab {
-moz-window-dragging: no-drag;
}

272
scss/win/_titleBar.scss Normal file
View file

@ -0,0 +1,272 @@
#toolbar-menubar {
// Allow right-click context menu on menubar
pointer-events: none;
visibility: visible;
// Space for 36*36 icon
padding-left: var(--tab-min-height);
}
#menubar-items {
pointer-events: all;
}
#titlebar {
pointer-events: none;
height: var(--tab-min-height);
margin-bottom: calc(0px - var(--tab-min-height));
}
.titlebar-buttonbox {
pointer-events: all;
}
#navigator-toolbox {
height :var(--tab-min-height);
-moz-box-pack: center;
pointer-events: none;
border-bottom: var(--material-border);
menubar {
height :var(--tab-min-height);
padding: 5px 1px 5px 1px !important;
}
menu {
border-radius: 4px;
}
#main-menubar > menu {
padding: 0px 4px 0px 4px;
}
#main-menubar > menu > menupopup {
// Align with menu bar bottom
margin-top: 0px;
}
}
.titlebar-icon-container {
margin-bottom: calc(0px - var(--tab-min-height));
height :var(--tab-min-height);
}
.titlebar-icon {
-moz-window-dragging: no-drag;
pointer-events: all;
// to make the icon 36*36
margin: 10px;
object-fit: none;
}
.titlebar-buttonbox-container {
-moz-box-pack: end;
height :var(--tab-min-height);
background: var(--material-tabbar);
}
// Following rules from https://searchfox.org/mozilla-central/source/browser/themes/windows/browser.css
:root {
--sidebar-border-color: ThreeDLightShadow;
}
#menubar-items {
flex-direction: column; /* for flex hack */
justify-content: normal; /* align the menubar to the top also in customize mode */
}
#main-menubar > menu {
appearance: none;
color: inherit;
&[_moz-menuactive] {
// background-color: light-dark(hsla(0,0%,0%,.12), hsla(0,0%,100%,.22));
@include light-dark(background-color, hsla(0,0%,0%,.12), hsla(0,0%,100%,.22));
color: inherit;
@media (prefers-contrast) {
background-color: -moz-menuhover;
color: -moz-menuhovertext;
}
}
}
@media (-moz-windows-accent-color-in-titlebar) {
:root[tabsintitlebar] {
/* stylelint-disable-next-line media-query-no-invalid */
@media (-moz-bool-pref: "browser.theme.windows.accent-color-in-tabs.enabled") {
--toolbox-non-lwt-bgcolor: ActiveCaption;
--toolbox-non-lwt-textcolor: CaptionText;
--toolbox-non-lwt-bgcolor-inactive: InactiveCaption;
--toolbox-non-lwt-textcolor-inactive: InactiveCaptionText;
#TabsToolbar:not(:-moz-lwtheme) {
/* These colors match the Linux/HCM default button colors. We need to
* override these on the tabs toolbar because the accent color is
* arbitrary, so the hardcoded colors from browser-custom-colors might
* not provide sufficient contrast. */
--toolbarbutton-icon-fill: currentColor;
--toolbarbutton-hover-background: color-mix(in srgb, currentColor 17%, transparent);
--toolbarbutton-active-background: color-mix(in srgb, currentColor 30%, transparent);
}
}
&[sizemode="normal"] #navigator-toolbox {
border-top: .5px solid ActiveBorder;
&:-moz-window-inactive {
border-top-color: InactiveBorder;
}
}
}
}
/* Titlebar */
.titlebar-buttonbox {
appearance: none;
/* The button box must appear on top of the navigator-toolbox in order for
* click and hover mouse events to work properly for the button in the restored
* window state. Otherwise, elements in the navigator-toolbox, like the menubar,
* can swallow those events. It will also place the buttons above the fog on
* Windows 7 with Aero Glass.
*/
z-index: 1;
}
.titlebar-buttonbox-container {
align-items: stretch;
/* Prevent window controls from overlapping the nav bar's shadow on the tab
* bar. */
#TabsToolbar > & {
margin-bottom: var(--tabs-navbar-shadow-size);
}
}
/* Window control buttons */
.titlebar-button {
appearance: none;
border: none;
margin: 0;
padding: 8px 17px;
-moz-context-properties: stroke;
stroke: currentColor;
:root[tabletmode] & {
display: none;
}
> .toolbarbutton-icon {
width: 12px;
height: 12px;
&:-moz-locale-dir(rtl) {
transform: scaleX(-1);
}
}
}
.titlebar-min {
/* Even though we use appearance: none, -moz-default-appearance is necessary
* for Windows 11's "snap layouts" feature, see
* DealWithWindowsAppearanceHacks */
-moz-default-appearance: -moz-window-button-minimize;
list-style-image: url(chrome://browser/skin/window-controls/minimize.svg);
}
.titlebar-max {
-moz-default-appearance: -moz-window-button-maximize;
list-style-image: url(chrome://browser/skin/window-controls/maximize.svg);
}
.titlebar-restore {
-moz-default-appearance: -moz-window-button-restore;
list-style-image: url(chrome://browser/skin/window-controls/restore.svg);
}
.titlebar-close {
-moz-default-appearance: -moz-window-button-close;
list-style-image: url(chrome://browser/skin/window-controls/close.svg);
}
:root[lwtheme-image] {
.titlebar-button {
-moz-context-properties: unset;
}
.titlebar-min {
list-style-image: url(chrome://browser/skin/window-controls/minimize-themes.svg);
}
.titlebar-max {
list-style-image: url(chrome://browser/skin/window-controls/maximize-themes.svg);
}
.titlebar-restore {
list-style-image: url(chrome://browser/skin/window-controls/restore-themes.svg);
}
.titlebar-close {
list-style-image: url(chrome://browser/skin/window-controls/close-themes.svg);
}
}
/* the 12px image renders a 10px icon, and the 10px upscaled gets rounded to 12.5, which
* rounds up to 13px, which makes the icon one pixel too big on 1.25dppx. Fix: */
@media (1.20dppx <= resolution <= 1.45dppx) {
.titlebar-button > .toolbarbutton-icon {
width: 11.5px;
height: 11.5px;
}
}
/* 175% dpi should result in the same device pixel sizes as 150% dpi. */
@media (1.70dppx <= resolution <= 1.95dppx) {
.titlebar-button {
padding-inline: 14.1px;
> .toolbarbutton-icon {
width: 10.8px;
height: 10.8px;
}
}
}
/* 225% dpi should result in the same device pixel sizes as 200% dpi. */
@media (2.20dppx <= resolution <= 2.45dppx) {
.titlebar-button {
padding-inline: 15.3333px;
> .toolbarbutton-icon {
width: 10.8px;
height: 10.8px;
}
}
}
/* 275% dpi should result in the same device pixel sizes as 250% dpi. */
@media (2.70dppx <= resolution <= 2.95dppx) {
/* NB: todo: this should also change padding on the buttons
* themselves, but without a device to test this on, it's
* impossible to know by how much. */
.titlebar-button > .toolbarbutton-icon {
width: 10.8px;
height: 10.8px;
}
}
// End of browser.css
#main-menubar > menu[_moz-menuactive="true"],
.titlebar-button:hover {
@include light-dark(background-color, hsla(0,0%,0%,.12), hsla(0,0%,100%,.22));
}
.titlebar-button:hover:active {
@include light-dark(background-color, hsla(0,0%,0%,.22), hsla(0,0%,100%,.32));
}
.titlebar-close:hover {
stroke: white;
background-color: hsl(355,86%,49%);
}
.titlebar-close:hover:active {
background-color: hsl(355,82%,69%);
}
.titlebar-button:not(:hover) > .toolbarbutton-icon:-moz-window-inactive {
opacity: 0.5;
}

View file

@ -12,6 +12,8 @@
@import "linux/item-tree";
@import "linux/menupopup";
@import "linux/search";
@import "linux/tabBar";
@import "linux/titleBar";
@import "linux/tagsBox";
@import "linux/tagSelector";
@import "linux/virtualized-table";

View file

@ -6,6 +6,9 @@
@import "win/createParent";
@import "win/search";
@import "win/tabBar";
@import "win/titleBar";
@import "win/menupopup";
@import "win/tag-selector";
@import "win/item-tree";
@import "win/virtualized-table";