mirror of
https://github.com/zen-browser/desktop.git
synced 2026-05-28 07:45:09 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11af62c94e | ||
|
|
4f0e7daa13 | ||
|
|
c6e8b0d3d9 | ||
|
|
8987c48abe | ||
|
|
492d34a9fa | ||
|
|
42ac8e6094 | ||
|
|
7dbe5b414d | ||
|
|
55c079d4ba | ||
|
|
a629866c28 | ||
|
|
6287f1a118 | ||
|
|
420d4ec064 | ||
|
|
908b164996 | ||
|
|
cfbf8edfa9 | ||
|
|
6f9aa2472b | ||
|
|
0ac56e9d04 | ||
|
|
544fd480b1 |
@@ -46,5 +46,6 @@ tabbrowser-reset-pin-button =
|
||||
zen-tab-sublabel =
|
||||
{ $tabSubtitle ->
|
||||
[zen-default-pinned] Back to pinned url
|
||||
[zen-default-pinned-cmd] Separate from pinned tab
|
||||
*[other] { $tabSubtitle }
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ zen-workspaces-panel-context-default-profile =
|
||||
zen-workspaces-panel-unload =
|
||||
.label = Unload Space
|
||||
|
||||
zen-workspaces-panel-unload-others =
|
||||
.label = Unload All Other Spaces
|
||||
|
||||
zen-workspaces-how-to-reorder-title = How to reorder spaces
|
||||
zen-workspaces-how-to-reorder-desc = Drag the space icons at the bottom of the sidebar to reorder them
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/.stylelintignore b/.stylelintignore
|
||||
index 185490999507b8a5032977237af50f5e61c71df1..e887fafa90b881e852a287ed8898638c995861ab 100644
|
||||
index 185490999507b8a5032977237af50f5e61c71df1..77c1635d90c6480af4ebd3c6e3ec49780eaca571 100644
|
||||
--- a/.stylelintignore
|
||||
+++ b/.stylelintignore
|
||||
@@ -106,3 +106,19 @@ build/pgo/blueprint/**/*.css
|
||||
@@ -17,8 +17,8 @@ index 185490999507b8a5032977237af50f5e61c71df1..e887fafa90b881e852a287ed8898638c
|
||||
+zen/common/styles/zen-theme.css
|
||||
+zen/compact-mode/zen-compact-mode.css
|
||||
+
|
||||
+zen/split-view/zen-decks.css
|
||||
+zen/workspaces/zen-workspaces.css
|
||||
+zen/split-view/zen-split-view.css
|
||||
+zen/spaces/zen-workspaces.css
|
||||
+zen/common/styles/zen-toolbar.css
|
||||
+
|
||||
+*.inc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml
|
||||
index 2faed30e09511c381051bc40910a883d1d7bc10d..3b8c89902502aa384473dd6f43be7ec49bad06ac 100644
|
||||
index 2faed30e09511c381051bc40910a883d1d7bc10d..959fa83f647a8919641c5b852a4cb8814fca9ab5 100644
|
||||
--- a/browser/base/content/browser-box.inc.xhtml
|
||||
+++ b/browser/base/content/browser-box.inc.xhtml
|
||||
@@ -3,6 +3,9 @@
|
||||
@@ -17,7 +17,7 @@ index 2faed30e09511c381051bc40910a883d1d7bc10d..3b8c89902502aa384473dd6f43be7ec4
|
||||
</vbox>
|
||||
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
|
||||
+<vbox flex="1" id="zen-appcontent-wrapper">
|
||||
+ <html:div id="zen-appcontent-navbar-wrapper">
|
||||
+ <html:div id="zen-appcontent-navbar-wrapper" class="chromeclass-location">
|
||||
+ <html:div id="zen-appcontent-navbar-container"></html:div>
|
||||
+ </html:div>
|
||||
+ <hbox id="zen-tabbox-wrapper" flex="1">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
|
||||
index e2e0526a0ddd617291f1f6c17bcfb807954b481f..c3d2afff6eaa788309d1c1a7fa40f9b8b4f0fffe 100644
|
||||
index e2e0526a0ddd617291f1f6c17bcfb807954b481f..1373fe072b3c74a52413859d4ad3612cbe1a2bda 100644
|
||||
--- a/browser/base/content/browser.js
|
||||
+++ b/browser/base/content/browser.js
|
||||
@@ -33,6 +33,7 @@ ChromeUtils.defineESModuleGetters(this, {
|
||||
@@ -28,7 +28,7 @@ index e2e0526a0ddd617291f1f6c17bcfb807954b481f..c3d2afff6eaa788309d1c1a7fa40f9b8
|
||||
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
|
||||
TranslationsParent.onLocationChange(gBrowser.selectedBrowser);
|
||||
|
||||
+ gZenPinnedTabManager.onLocationChange(gBrowser.selectedBrowser);
|
||||
+ gZenPinnedTabManager.onLocationChange(gBrowser.selectedBrowser, location);
|
||||
+
|
||||
PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-browser-container.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-omnibox.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-workspaces.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-decks.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-split-view.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-folders.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-glance.css" />
|
||||
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-popup.css" />
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
# Scripts used all over the browser
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenMediaController.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenWorkspaceCreation.mjs"></script>
|
||||
<script type="module" src="resource:///modules/zen/ZenSpaceCreation.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenGlanceManager.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenPinnedTabManager.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenViewSplitter.mjs"></script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include ../../../zen/drag-and-drop/jar.inc.mn
|
||||
#include ../../../zen/split-view/jar.inc.mn
|
||||
#include ../../../zen/mods/jar.inc.mn
|
||||
#include ../../../zen/workspaces/jar.inc.mn
|
||||
#include ../../../zen/spaces/jar.inc.mn
|
||||
#include ../../../zen/tabs/jar.inc.mn
|
||||
#include ../../../zen/kbs/jar.inc.mn
|
||||
#include ../../../zen/glance/jar.inc.mn
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
<command id="cmd_zenCtxDeleteWorkspace" />
|
||||
<command id="cmd_zenUnloadWorkspace" />
|
||||
<command id="cmd_zenUnloadAllOtherWorkspace" />
|
||||
<command id="cmd_zenChangeWorkspaceName" />
|
||||
<command id="cmd_zenChangeWorkspaceIcon" />
|
||||
<command id="cmd_zenReorderWorkspaces" />
|
||||
|
||||
@@ -42,10 +42,11 @@
|
||||
hide-if-usercontext-disabled="true">
|
||||
<menupopup />
|
||||
</menu>
|
||||
<menuitem id="context_zenUnloadWorkspace" data-l10n-id="zen-workspaces-panel-unload" command="cmd_zenUnloadWorkspace"/>
|
||||
<menuseparator id="context_zenWorkspacesSeparator"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_zenReorderWorkspaces" data-l10n-id="zen-workspaces-panel-context-reorder" command="cmd_zenReorderWorkspaces"/>
|
||||
<menuitem id="context_zenUnloadWorkspace" data-l10n-id="zen-workspaces-panel-unload" command="cmd_zenUnloadWorkspace"/>
|
||||
<menuitem id="context_zenUnloadAllOtherWorkspace" data-l10n-id="zen-workspaces-panel-unload-others" command="cmd_zenUnloadAllOtherWorkspace"/>
|
||||
<menuseparator/>
|
||||
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation"/>
|
||||
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete" command="cmd_zenCtxDeleteWorkspace"/>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
# They must all go from the middle to the right side. They must always stay verically centered.
|
||||
# And reach to 180 on the right side, meaning we must divide the width in 16 segments.
|
||||
<box data-type="explicit-black-white" data-algo="float" data-num-dots="1"
|
||||
data-position="337.5,180" style="background: rgb(224, 224, 224);"></box>
|
||||
data-position="340,180" style="background: rgb(224, 224, 224);"></box>
|
||||
<box data-type="explicit-black-white" data-algo="float" data-num-dots="1"
|
||||
data-position="337.5,180" style="background: rgb(224, 224, 224);"></box>
|
||||
<box data-type="explicit-black-white" data-algo="float" data-num-dots="1"
|
||||
@@ -131,11 +131,11 @@
|
||||
</box>
|
||||
<html:input type="range" value="0.4" step="0.001" id="PanelUI-zen-gradient-generator-opacity"
|
||||
#ifdef XP_MACOSX
|
||||
max="0.75"
|
||||
min="0.35"
|
||||
max="0.9"
|
||||
min="0.30"
|
||||
#else
|
||||
max="0.9"
|
||||
min="0.35"
|
||||
min="0.25"
|
||||
#endif
|
||||
/>
|
||||
</vbox>
|
||||
@@ -2,7 +2,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include zen-panels/gradient-generator.inc
|
||||
#include zen-panels/theme-picker.inc
|
||||
#include zen-panels/emojis-picker.inc
|
||||
#include zen-panels/folders-search.inc
|
||||
#include zen-panels/site-data.inc
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-sets.js"></script>
|
||||
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenHasPolyfill.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenWorkspaces.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenWorkspace.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/zen-components/ZenWorkspaceIcons.mjs"></script>
|
||||
<script type="module" src="resource:///modules/zen/ZenSpaceManager.mjs"></script>
|
||||
<script type="module" src="resource:///modules/zen/ZenSpace.mjs"></script>
|
||||
<script type="module" src="resource:///modules/zen/ZenSpaceIcons.mjs"></script>
|
||||
@@ -15,7 +15,7 @@ index 4aad4e4fb4139aa3d81e00eefa82e26b697df973..831e42a4a55e277b5b8e81e4317a2007
|
||||
src="chrome://browser/locale/places/bookmarkProperties.properties"/>
|
||||
</stringbundleset>
|
||||
|
||||
+ <script src="chrome://browser/content/zen-components/ZenWorkspaceBookmarksStorage.js" />
|
||||
+ <script src="chrome://browser/content/zen-components/ZenSpaceBookmarksStorage.js" />
|
||||
+ <script src="chrome://browser/content/zenThemeModifier.js"></script>
|
||||
<script src="chrome://browser/content/places/editBookmark.js"/>
|
||||
<script src="chrome://browser/content/places/bookmarkProperties.js"/>
|
||||
|
||||
@@ -1020,8 +1020,24 @@ var gZenCKSSettings = {
|
||||
let shortcut;
|
||||
if (event.code && event.code.startsWith("Key")) {
|
||||
shortcut = event.code.slice(3);
|
||||
} else if (event.code && event.code.startsWith("Digit")) {
|
||||
shortcut = event.code.slice(5);
|
||||
} else {
|
||||
shortcut = event.key;
|
||||
// Use physical key mapping for common symbols
|
||||
const CODE_TO_KEY_MAP = {
|
||||
Comma: ",",
|
||||
Period: ".",
|
||||
Slash: "/",
|
||||
Semicolon: ";",
|
||||
Quote: "'",
|
||||
BracketLeft: "[",
|
||||
BracketRight: "]",
|
||||
Backslash: "\\",
|
||||
Backquote: "`",
|
||||
Minus: "-",
|
||||
Equal: "=",
|
||||
};
|
||||
shortcut = CODE_TO_KEY_MAP[event.code] || event.key;
|
||||
}
|
||||
|
||||
shortcut = shortcut.replace(/Ctrl|Control|Shift|Alt|Option|Cmd|Meta/, ""); // Remove all modifiers
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
|
||||
index 836bee14d2b63604688ebe477a5d915a5e99b305..7e105a1ae07657b0a0e664a8e3d9d2eb894fa1d4 100644
|
||||
index 836bee14d2b63604688ebe477a5d915a5e99b305..5f60aa3bedd4f80b887ea3e050fd86a21a6b280a 100644
|
||||
--- a/browser/components/tabbrowser/content/tab.js
|
||||
+++ b/browser/components/tabbrowser/content/tab.js
|
||||
@@ -21,6 +21,7 @@
|
||||
@@ -110,7 +110,26 @@ index 836bee14d2b63604688ebe477a5d915a5e99b305..7e105a1ae07657b0a0e664a8e3d9d2eb
|
||||
}
|
||||
|
||||
get splitview() {
|
||||
@@ -489,6 +515,8 @@
|
||||
@@ -444,6 +470,10 @@
|
||||
: this;
|
||||
gBrowser.warmupTab(tabToWarm);
|
||||
|
||||
+ if (event.target.classList.contains("tab-reset-pin-button")) {
|
||||
+ gZenPinnedTabManager.onResetPinButtonMouseOver(this, event);
|
||||
+ }
|
||||
+
|
||||
// If the previous target wasn't part of this tab then this is a mouseenter event.
|
||||
if (!this.contains(event.relatedTarget)) {
|
||||
this._mouseenter();
|
||||
@@ -455,6 +485,7 @@
|
||||
if (!this.contains(event.relatedTarget)) {
|
||||
this._mouseleave();
|
||||
}
|
||||
+ gZenPinnedTabManager.onResetPinButtonMouseOut(this);
|
||||
}
|
||||
|
||||
on_dragstart(event) {
|
||||
@@ -489,6 +520,8 @@
|
||||
this.style.MozUserFocus = "ignore";
|
||||
} else if (
|
||||
event.target.classList.contains("tab-close-button") ||
|
||||
@@ -119,7 +138,7 @@ index 836bee14d2b63604688ebe477a5d915a5e99b305..7e105a1ae07657b0a0e664a8e3d9d2eb
|
||||
event.target.classList.contains("tab-icon-overlay") ||
|
||||
event.target.classList.contains("tab-audio-button")
|
||||
) {
|
||||
@@ -543,6 +571,10 @@
|
||||
@@ -543,16 +576,21 @@
|
||||
this.style.MozUserFocus = "";
|
||||
}
|
||||
|
||||
@@ -130,7 +149,40 @@ index 836bee14d2b63604688ebe477a5d915a5e99b305..7e105a1ae07657b0a0e664a8e3d9d2eb
|
||||
on_click(event) {
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
@@ -603,6 +635,14 @@
|
||||
}
|
||||
|
||||
- if (event.getModifierState("Accel") || event.shiftKey) {
|
||||
+ if (event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
+ !event.getModifierState("Accel") &&
|
||||
gBrowser.multiSelectedTabsCount > 0 &&
|
||||
!event.target.classList.contains("tab-close-button") &&
|
||||
!event.target.classList.contains("tab-icon-overlay") &&
|
||||
@@ -564,8 +602,9 @@
|
||||
}
|
||||
|
||||
if (
|
||||
- event.target.classList.contains("tab-icon-overlay") ||
|
||||
- event.target.classList.contains("tab-audio-button")
|
||||
+ !event.getModifierState("Accel") &&
|
||||
+ (event.target.classList.contains("tab-icon-overlay") ||
|
||||
+ event.target.classList.contains("tab-audio-button"))
|
||||
) {
|
||||
if (this.activeMediaBlocked) {
|
||||
if (this.multiselected) {
|
||||
@@ -583,7 +622,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
- if (event.target.classList.contains("tab-close-button")) {
|
||||
+ if (!event.getModifierState("Accel") && event.target.classList.contains("tab-close-button")) {
|
||||
if (this.multiselected) {
|
||||
gBrowser.removeMultiSelectedTabs(
|
||||
lazy.TabMetrics.userTriggeredContext(
|
||||
@@ -603,6 +642,14 @@
|
||||
// (see tabbrowser-tabs 'click' handler).
|
||||
gBrowser.tabContainer._blockDblClick = true;
|
||||
}
|
||||
@@ -138,14 +190,14 @@ index 836bee14d2b63604688ebe477a5d915a5e99b305..7e105a1ae07657b0a0e664a8e3d9d2eb
|
||||
+ if (event.target.classList.contains("tab-reset-pin-button")) {
|
||||
+ gZenPinnedTabManager._onTabResetPinButton(event, this, 'reset');
|
||||
+ gBrowser.tabContainer._blockDblClick = true;
|
||||
+ } else if (event.target.classList.contains("tab-reset-button")) {
|
||||
+ } else if (!event.getModifierState("Accel") && event.target.classList.contains("tab-reset-button")) {
|
||||
+ gZenPinnedTabManager.onCloseTabShortcut(event, this);
|
||||
+ gBrowser.tabContainer._blockDblClick = true;
|
||||
+ }
|
||||
}
|
||||
|
||||
on_dblclick(event) {
|
||||
@@ -626,6 +666,8 @@
|
||||
@@ -626,6 +673,8 @@
|
||||
animate: true,
|
||||
triggeringEvent: event,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/toolkit/content/widgets/arrowscrollbox.js b/toolkit/content/widgets/arrowscrollbox.js
|
||||
index b80d1049bb6ae305f2ac9c4c35fe975fd508031c..9bc80194d4e35f663b5c15baae55fa48eed4ffe9 100644
|
||||
index b80d1049bb6ae305f2ac9c4c35fe975fd508031c..be2cbdb20cb2064459b6f7bef56fd0470c3b7f40 100644
|
||||
--- a/toolkit/content/widgets/arrowscrollbox.js
|
||||
+++ b/toolkit/content/widgets/arrowscrollbox.js
|
||||
@@ -98,6 +98,7 @@
|
||||
@@ -15,7 +15,7 @@ index b80d1049bb6ae305f2ac9c4c35fe975fd508031c..9bc80194d4e35f663b5c15baae55fa48
|
||||
on_wheel(event) {
|
||||
// Don't consume the event if we can't scroll.
|
||||
- if (!this.overflowing) {
|
||||
+ if (!this.overflowing || this.id === 'tabbrowser-arrowscrollbox' || ((event.deltaY == 0 || window.gZenWorkspaces?._swipeState?.isGestureActive) && this.classList.contains('workspace-arrowscrollbox'))) {
|
||||
+ if (!this.overflowing || this.id === 'tabbrowser-arrowscrollbox' || ((event.deltaY == 0 || window.gZenWorkspaces?._swipeManager?.isGestureActive) && this.classList.contains('workspace-arrowscrollbox'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// prettier-ignore
|
||||
// eslint-disable-next-line no-lone-blocks
|
||||
{
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenWorkspaceBookmarksStorage.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenSpaceBookmarksStorage.js", this);
|
||||
|
||||
ChromeUtils.importESModule("chrome://browser/content/ZenStartup.mjs", { global: "current" });
|
||||
ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenCompactMode.mjs", { global: "current" });
|
||||
|
||||
@@ -76,6 +76,10 @@ window.gZenUIManager = {
|
||||
this._initBookmarkCollapseListener();
|
||||
|
||||
gURLBar._setPlaceholder(null);
|
||||
|
||||
document
|
||||
.getElementById("PersonalToolbar")
|
||||
.setAttribute("fullscreentoolbar", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -500,6 +504,12 @@ window.gZenUIManager = {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close the new tab popup on cmd/ctrl + t
|
||||
if (!overridePreferance && gURLBar.hasAttribute("zen-newtab")) {
|
||||
this.handleUrlbarClose();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear any existing timeout
|
||||
if (this._clearTimeout) {
|
||||
clearTimeout(this._clearTimeout);
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root[zen-show-grainy-background="true"] & .zen-browser-grain {
|
||||
&[zen-show-grainy-background="true"] .zen-browser-grain {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -129,6 +129,10 @@ document.addEventListener(
|
||||
gZenWorkspaces.unloadWorkspace();
|
||||
break;
|
||||
}
|
||||
case "cmd_zenUnloadAllOtherWorkspace": {
|
||||
gZenWorkspaces.unloadAllOtherWorkspaces();
|
||||
break;
|
||||
}
|
||||
case "cmd_zenNewNavigatorUnsynced":
|
||||
OpenBrowserWindow({ zenSyncedWindow: false });
|
||||
break;
|
||||
|
||||
@@ -828,7 +828,7 @@
|
||||
|
||||
#createFakeTabSplit(dropElement, dropSide) {
|
||||
// Remove drop indicator
|
||||
this.clearDragOverVisuals();
|
||||
this.clearDragOverVisuals({ clearSplitDropIndicator: false });
|
||||
|
||||
// Remove any existing fake tab
|
||||
if (this.#dragOverSplit.fakeTab) {
|
||||
@@ -1191,8 +1191,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
clearDragOverVisuals() {
|
||||
clearDragOverVisuals({ clearSplitDropIndicator = true } = {}) {
|
||||
this.#removeDragOverBackground();
|
||||
if (clearSplitDropIndicator) {
|
||||
this._clearDragOverSplit();
|
||||
}
|
||||
gZenPinnedTabManager.removeTabContainersDragoverClass();
|
||||
}
|
||||
|
||||
@@ -1217,6 +1220,10 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
_moveTogetherSelectedTabs() {
|
||||
// Override the default behavior of only moving together selected tabs.
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
#applyDragoverIndicator(event, dropElement, movingTabs, draggedTab) {
|
||||
// Doesn't show indicator when dragOverSplit
|
||||
|
||||
@@ -17,4 +17,5 @@ DIRS += [
|
||||
"toolkit",
|
||||
"sessionstore",
|
||||
"share",
|
||||
"spaces",
|
||||
]
|
||||
|
||||
@@ -710,18 +710,35 @@ export class nsZenSessionManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out tabs that are not useful to restore, such as empty tabs with no group association.
|
||||
* If removeUnpinnedTabs is true, it also filters out unpinned tabs.
|
||||
* Determines whether a tab should be collected based on its data.
|
||||
*
|
||||
* @param {Array} tabs - The array of tab data objects to filter.
|
||||
* @returns {Array} The filtered array of tab data objects.
|
||||
* @param {object} tabData - The tab data object to evaluate.
|
||||
* @returns {boolean} True if the tab should be collected, false otherwise.
|
||||
*/
|
||||
#filterUnusedTabs(tabs) {
|
||||
return tabs.filter(tab => {
|
||||
// We need to ignore empty tabs with no group association
|
||||
// as they are not useful to restore.
|
||||
return !(tab.zenIsEmpty && !tab.groupId);
|
||||
});
|
||||
#shouldCollectTab(tabData) {
|
||||
return tabData && !(tabData.zenIsEmpty && !tabData.groupId);
|
||||
}
|
||||
|
||||
#collectUsedTabsFromWindows(aStateWindows) {
|
||||
const tabIdRelationMap = new Map();
|
||||
for (const window of aStateWindows) {
|
||||
// Only accept the tabs with `_zenIsActiveTab` set to true from
|
||||
// every window. We do this to avoid collecting tabs with invalid
|
||||
// state when multiple windows are open. Note that if we a tab without
|
||||
// this flag set in any other window, we just add it anyway.
|
||||
for (const tabData of window.tabs || []) {
|
||||
if (!this.#shouldCollectTab(tabData)) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!tabIdRelationMap.has(tabData.zenSyncId) ||
|
||||
tabData._zenIsActiveTab
|
||||
) {
|
||||
tabIdRelationMap.set(tabData.zenSyncId, tabData);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Array.from(tabIdRelationMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -731,25 +748,7 @@ export class nsZenSessionManager {
|
||||
* @param {object} aStateWindows The array of window state objects.
|
||||
*/
|
||||
#collectTabsData(sidebarData, aStateWindows) {
|
||||
const tabIdRelationMap = new Map();
|
||||
for (const window of aStateWindows) {
|
||||
// Only accept the tabs with `_zenIsActiveTab` set to true from
|
||||
// every window. We do this to avoid collecting tabs with invalid
|
||||
// state when multiple windows are open. Note that if we a tab without
|
||||
// this flag set in any other window, we just add it anyway.
|
||||
for (const tabData of window.tabs || []) {
|
||||
if (
|
||||
!tabIdRelationMap.has(tabData.zenSyncId) ||
|
||||
tabData._zenIsActiveTab
|
||||
) {
|
||||
tabIdRelationMap.set(tabData.zenSyncId, tabData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sidebarData.tabs = this.#filterUnusedTabs(
|
||||
Array.from(tabIdRelationMap.values())
|
||||
);
|
||||
sidebarData.tabs = this.#collectUsedTabsFromWindows(aStateWindows);
|
||||
|
||||
let firstWindow = aStateWindows[0];
|
||||
sidebarData.folders = firstWindow.folders;
|
||||
@@ -826,19 +825,31 @@ export class nsZenSessionManager {
|
||||
return;
|
||||
}
|
||||
this.log("Restoring new window with Zen session data");
|
||||
const state = lazy.SessionStore.getCurrentState(true);
|
||||
const windows = (state.windows || []).filter(
|
||||
win =>
|
||||
!win.isPrivate &&
|
||||
!win.isPopup &&
|
||||
!win.isTaskbarTab &&
|
||||
!win.isZenUnsynced
|
||||
);
|
||||
void lazy.SessionStore.getCurrentState(true);
|
||||
// We want to iterate all windows except from aWindow.__SSi (string).
|
||||
// SessionStoreInternal._windows is an object, with the ID as key and the
|
||||
// window data as value, so we need to filter out the values that have the
|
||||
// same ID as aWindow.__SSi. but lets filter it into an array to make it easier to work with.
|
||||
let windows = [];
|
||||
for (const winKey of Object.keys(SessionStoreInternal._windows)) {
|
||||
const winData = SessionStoreInternal._windows[winKey];
|
||||
if (
|
||||
winData &&
|
||||
winKey !== aWindow.__SSi &&
|
||||
!winData.isPrivate &&
|
||||
!winData.isPopup &&
|
||||
!winData.isTaskbarTab &&
|
||||
!winData.isZenUnsynced
|
||||
) {
|
||||
windows.push(winData);
|
||||
}
|
||||
}
|
||||
let windowToClone = windows[0] || {};
|
||||
let newWindow = Cu.cloneInto(windowToClone, {});
|
||||
newWindow.tabs = this.#collectUsedTabsFromWindows(windows);
|
||||
let shouldRestoreOnlyPinned =
|
||||
!lazy.gWindowSyncEnabled || lazy.gSyncOnlyPinnedTabs;
|
||||
if (windows.length < 2) {
|
||||
if (windows.length < 1) {
|
||||
// We only want to restore the sidebar object if we found
|
||||
// only one normal window to clone from (which is the one
|
||||
// we are opening).
|
||||
@@ -846,7 +857,6 @@ export class nsZenSessionManager {
|
||||
this.#restoreWindowData(newWindow);
|
||||
shouldRestoreOnlyPinned ||= this.#shouldRestoreOnlyPinned;
|
||||
}
|
||||
newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []);
|
||||
if (shouldRestoreOnlyPinned) {
|
||||
// Don't bring over any unpinned tabs if window sync is disabled or if syncing only pinned tabs.
|
||||
this.#filterUnpinnedTabs(newWindow);
|
||||
|
||||
@@ -909,15 +909,19 @@ class nsZenWindowSync {
|
||||
}
|
||||
);
|
||||
|
||||
let mySrc = await new Promise((r, re) => {
|
||||
let mySrc = await new Promise(r => {
|
||||
const reader = new FileReader();
|
||||
if (!browserBlob) {
|
||||
r("");
|
||||
return;
|
||||
}
|
||||
reader.readAsDataURL(browserBlob);
|
||||
reader.onloadend = function () {
|
||||
// result includes identifier 'data:image/png;base64,' plus the base64 data
|
||||
r(reader.result);
|
||||
};
|
||||
reader.onerror = function () {
|
||||
re(new Error("Failed to read blob as data URL"));
|
||||
r("");
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -1257,8 +1257,8 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
|
||||
let colorToBlend;
|
||||
let colorToBlendOpacity;
|
||||
if (this.isMica) {
|
||||
colorToBlend = !this.isDarkMode ? [0, 0, 0] : [255, 255, 255];
|
||||
colorToBlendOpacity = 0.35;
|
||||
colorToBlend = this.isDarkMode ? [0, 0, 0] : [255, 255, 255];
|
||||
colorToBlendOpacity = 0.25;
|
||||
} else if (AppConstants.platform === "macosx") {
|
||||
colorToBlend = [255, 255, 255];
|
||||
colorToBlendOpacity = 0.35;
|
||||
@@ -1271,7 +1271,7 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
|
||||
colorToBlendOpacity * (1 - (opacity + lazy.MIN_OPACITY))
|
||||
);
|
||||
baseColor = this.blendColors(baseColor, colorToBlend, blendedAlpha * 100);
|
||||
if (AppConstants.platform !== "macosx") {
|
||||
if (!this.canBeTransparent) {
|
||||
opacity += colorToBlendOpacity * (1 - opacity);
|
||||
}
|
||||
}
|
||||
@@ -1438,13 +1438,14 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
|
||||
}
|
||||
|
||||
updateNoise(texture) {
|
||||
document.documentElement.style.setProperty(
|
||||
"--zen-grainy-background-opacity",
|
||||
texture
|
||||
);
|
||||
document.documentElement.setAttribute(
|
||||
"zen-show-grainy-background",
|
||||
texture > 0 ? "true" : "false"
|
||||
[lazy.browserBackgroundElement, lazy.toolbarBackgroundElement].forEach(
|
||||
element => {
|
||||
element.style.setProperty("--zen-grainy-background-opacity", texture);
|
||||
element.setAttribute(
|
||||
"zen-show-grainy-background",
|
||||
texture > 0 ? "true" : "false"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
/* eslint-disable no-shadow */
|
||||
|
||||
import { nsZenThemePicker } from "chrome://browser/content/zen-components/ZenGradientGenerator.mjs";
|
||||
import { nsZenThemePicker } from "resource:///modules/zen/ZenGradientGenerator.mjs";
|
||||
import { ZenSpacesSwipe } from "resource:///modules/zen/ZenSpacesSwipe.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
@@ -40,12 +41,6 @@ class nsZenWorkspaces {
|
||||
#canDebug = Services.prefs.getBoolPref("zen.workspaces.debug", false);
|
||||
#activeWorkspace = "";
|
||||
|
||||
_swipeState = {
|
||||
isGestureActive: true,
|
||||
lastDelta: 0,
|
||||
direction: null,
|
||||
};
|
||||
|
||||
_workspaceCache = [];
|
||||
|
||||
#lastScrollTime = 0;
|
||||
@@ -194,7 +189,7 @@ class nsZenWorkspaces {
|
||||
this.workspaceEnabled &&
|
||||
!this.isPrivateWindow
|
||||
) {
|
||||
this.initializeGestureHandlers();
|
||||
this._swipeManager = new ZenSpacesSwipe();
|
||||
this.initializeWorkspaceNavigation();
|
||||
}
|
||||
}
|
||||
@@ -647,73 +642,9 @@ class nsZenWorkspaces {
|
||||
);
|
||||
}
|
||||
|
||||
initializeGestureHandlers() {
|
||||
const elements = [
|
||||
gNavToolbox,
|
||||
// event handlers do not work on elements inside shadow DOM so we need to attach them directly
|
||||
document
|
||||
.getElementById("tabbrowser-arrowscrollbox")
|
||||
.shadowRoot.querySelector("scrollbox"),
|
||||
];
|
||||
|
||||
// Attach gesture handlers to each element
|
||||
for (const element of elements) {
|
||||
if (!element) {
|
||||
continue;
|
||||
}
|
||||
this.attachGestureHandlers(element);
|
||||
}
|
||||
}
|
||||
|
||||
attachGestureHandlers(element) {
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureMayStart",
|
||||
this._handleSwipeMayStart.bind(this),
|
||||
true
|
||||
);
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureStart",
|
||||
this._handleSwipeStart.bind(this),
|
||||
true
|
||||
);
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureUpdate",
|
||||
this._handleSwipeUpdate.bind(this),
|
||||
true
|
||||
);
|
||||
|
||||
// Use MozSwipeGesture instead of MozSwipeGestureEnd because MozSwipeGestureEnd is fired after animation ends,
|
||||
// while MozSwipeGesture is fired immediately after swipe ends.
|
||||
element.addEventListener(
|
||||
"MozSwipeGesture",
|
||||
this._handleSwipeEnd.bind(this),
|
||||
true
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureEnd",
|
||||
() => {
|
||||
Services.prefs.setBoolPref("zen.swipe.is-fast-swipe", false);
|
||||
document.documentElement.removeAttribute("swipe-gesture");
|
||||
gZenUIManager.tabsWrapper.style.removeProperty("scrollbar-width");
|
||||
[lazy.browserBackgroundElement, lazy.toolbarBackgroundElement].forEach(
|
||||
element => {
|
||||
element.style.setProperty("--zen-background-opacity", "1");
|
||||
}
|
||||
);
|
||||
delete this._hasAnimatedBackgrounds;
|
||||
this.updateTabsContainers();
|
||||
document.removeEventListener("popupshown", this.popupOpenHandler, {
|
||||
once: true,
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
_popupOpenHandler() {
|
||||
// If a popup is opened, we should stop the swipe gesture
|
||||
if (this._swipeState?.isGestureActive) {
|
||||
if (this._swipeManager?.isGestureActive) {
|
||||
document.documentElement.removeAttribute("swipe-gesture");
|
||||
gZenUIManager.tabsWrapper.style.removeProperty("scrollbar-width");
|
||||
this.updateTabsContainers();
|
||||
@@ -721,113 +652,6 @@ class nsZenWorkspaces {
|
||||
}
|
||||
}
|
||||
|
||||
_handleSwipeMayStart(event) {
|
||||
if (this.privateWindowOrDisabled || this.#inChangingWorkspace) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
event.target.closest("#zen-sidebar-foot-buttons") ||
|
||||
event.target.closest('#urlbar[zen-floating-urlbar="true"]')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle horizontal swipes
|
||||
if (
|
||||
event.direction === event.DIRECTION_LEFT ||
|
||||
event.direction === event.DIRECTION_RIGHT
|
||||
) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Set allowed directions based on available workspaces
|
||||
event.allowedDirections |= event.DIRECTION_LEFT | event.DIRECTION_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
_handleSwipeStart(event) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
gZenFolders.cancelPopupTimer();
|
||||
|
||||
document.documentElement.setAttribute("swipe-gesture", "true");
|
||||
document.addEventListener("popupshown", this.popupOpenHandler, {
|
||||
once: true,
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this._swipeState = {
|
||||
isGestureActive: true,
|
||||
lastDelta: 0,
|
||||
direction: null,
|
||||
};
|
||||
Services.prefs.setBoolPref("zen.swipe.is-fast-swipe", true);
|
||||
}
|
||||
|
||||
_handleSwipeUpdate(event) {
|
||||
if (!this.workspaceEnabled || !this._swipeState?.isGestureActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const delta = event.delta * 300;
|
||||
const stripWidth =
|
||||
window.windowUtils.getBoundsWithoutFlushing(
|
||||
document.getElementById("navigator-toolbox")
|
||||
).width +
|
||||
window.windowUtils.getBoundsWithoutFlushing(
|
||||
document.getElementById("zen-sidebar-splitter")
|
||||
).width *
|
||||
2;
|
||||
let translateX = this._swipeState.lastDelta + delta;
|
||||
// Add a force multiplier as we are translating the strip depending on how close to the edge we are
|
||||
let forceMultiplier = Math.min(
|
||||
1,
|
||||
1 - Math.abs(translateX) / (stripWidth * 4.5)
|
||||
); // 4.5 instead of 4 to add a bit of a buffer
|
||||
if (forceMultiplier > 0.5) {
|
||||
translateX *= forceMultiplier;
|
||||
this._swipeState.lastDelta = delta + (translateX - delta) * 0.5;
|
||||
} else {
|
||||
translateX = this._swipeState.lastDelta;
|
||||
}
|
||||
|
||||
if (Math.abs(delta) > 0.8) {
|
||||
this._swipeState.direction = delta > 0 ? "left" : "right";
|
||||
}
|
||||
|
||||
// Apply a translateX to the tab strip to give the user feedback on the swipe
|
||||
const currentWorkspace = this.getActiveWorkspaceFromCache();
|
||||
this._organizeWorkspaceStripLocations(currentWorkspace, true, translateX);
|
||||
}
|
||||
|
||||
async _handleSwipeEnd(event) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const isRTL = document.documentElement.matches(":-moz-locale-dir(rtl)");
|
||||
const moveForward =
|
||||
(event.direction === SimpleGestureEvent.DIRECTION_RIGHT) !== isRTL;
|
||||
|
||||
const rawDirection = moveForward ? 1 : -1;
|
||||
const direction = this.naturalScroll ? -1 : 1;
|
||||
await this.changeWorkspaceShortcut(rawDirection * direction, true);
|
||||
|
||||
// Reset swipe state
|
||||
this._swipeState = {
|
||||
isGestureActive: false,
|
||||
lastDelta: 0,
|
||||
direction: null,
|
||||
};
|
||||
}
|
||||
|
||||
get activeWorkspace() {
|
||||
return this.#activeWorkspace;
|
||||
}
|
||||
@@ -847,6 +671,10 @@ class nsZenWorkspaces {
|
||||
Services.prefs.setStringPref("zen.workspaces.active", value);
|
||||
}
|
||||
|
||||
get isChangingWorkspace() {
|
||||
return this.#inChangingWorkspace;
|
||||
}
|
||||
|
||||
get shouldHaveWorkspaces() {
|
||||
if (typeof this._shouldHaveWorkspaces === "undefined") {
|
||||
let chromeFlags = window.docShell.treeOwner
|
||||
@@ -1665,6 +1493,21 @@ class nsZenWorkspaces {
|
||||
await gBrowser.explicitUnloadTabs(tabsToUnload); // TODO: unit test this
|
||||
}
|
||||
|
||||
async unloadAllOtherWorkspaces() {
|
||||
const workspaceId =
|
||||
this.#contextMenuData?.workspaceId || this.activeWorkspace;
|
||||
|
||||
const tabsToUnload = this.allStoredTabs.filter(
|
||||
tab =>
|
||||
tab.getAttribute("zen-workspace-id") !== workspaceId &&
|
||||
!tab.hasAttribute("zen-empty-tab") &&
|
||||
!tab.hasAttribute("zen-essential") &&
|
||||
!tab.hasAttribute("pending")
|
||||
);
|
||||
|
||||
await gBrowser.explicitUnloadTabs(tabsToUnload); // TODO: unit test this
|
||||
}
|
||||
|
||||
moveTabToWorkspace(tab, workspaceID) {
|
||||
return this.moveTabsToWorkspace([tab], workspaceID);
|
||||
}
|
||||
210
src/zen/spaces/ZenSpacesSwipe.mjs
Normal file
210
src/zen/spaces/ZenSpacesSwipe.mjs
Normal file
@@ -0,0 +1,210 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "browserBackgroundElement", () => {
|
||||
return document.getElementById("zen-browser-background");
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "toolbarBackgroundElement", () => {
|
||||
return document.getElementById("zen-toolbar-background");
|
||||
});
|
||||
|
||||
export class ZenSpacesSwipe {
|
||||
_swipeState = {
|
||||
isGestureActive: false,
|
||||
lastDelta: 0,
|
||||
direction: null,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
const elements = [
|
||||
gNavToolbox,
|
||||
// Event handlers do not work on elements inside shadow DOM
|
||||
// so we need to attach them directly.
|
||||
document
|
||||
.getElementById("tabbrowser-arrowscrollbox")
|
||||
?.shadowRoot?.querySelector("scrollbox"),
|
||||
];
|
||||
|
||||
for (const element of elements) {
|
||||
if (!element) {
|
||||
continue;
|
||||
}
|
||||
this._attachWorkspaceSwipeGestures(element);
|
||||
}
|
||||
}
|
||||
|
||||
_attachWorkspaceSwipeGestures(element) {
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureMayStart",
|
||||
this._handleSwipeMayStart.bind(this),
|
||||
true
|
||||
);
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureStart",
|
||||
this._handleSwipeStart.bind(this),
|
||||
true
|
||||
);
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureUpdate",
|
||||
this._handleSwipeUpdate.bind(this),
|
||||
true
|
||||
);
|
||||
|
||||
// Use MozSwipeGesture instead of MozSwipeGestureEnd because MozSwipeGestureEnd is fired after animation ends,
|
||||
// while MozSwipeGesture is fired immediately after swipe ends.
|
||||
element.addEventListener(
|
||||
"MozSwipeGesture",
|
||||
this._handleSwipeEnd.bind(this),
|
||||
true
|
||||
);
|
||||
|
||||
element.addEventListener(
|
||||
"MozSwipeGestureEnd",
|
||||
() => {
|
||||
this.onSwipeGestureAnimationEnd();
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
_handleSwipeMayStart(event) {
|
||||
const ws = gZenWorkspaces;
|
||||
|
||||
if (ws.privateWindowOrDisabled || ws.isChangingWorkspace) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
event.target.closest("#zen-sidebar-foot-buttons") ||
|
||||
event.target.closest('#urlbar[zen-floating-urlbar="true"]')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle horizontal swipes
|
||||
if (
|
||||
event.direction === event.DIRECTION_LEFT ||
|
||||
event.direction === event.DIRECTION_RIGHT
|
||||
) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Set allowed directions based on available workspaces
|
||||
event.allowedDirections |= event.DIRECTION_LEFT | event.DIRECTION_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
_handleSwipeStart(event) {
|
||||
const ws = gZenWorkspaces;
|
||||
|
||||
if (!ws.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
gZenFolders.cancelPopupTimer();
|
||||
|
||||
document.documentElement.setAttribute("swipe-gesture", "true");
|
||||
document.addEventListener("popupshown", ws.popupOpenHandler, {
|
||||
once: true,
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this._swipeState = {
|
||||
isGestureActive: true,
|
||||
lastDelta: 0,
|
||||
direction: null,
|
||||
};
|
||||
Services.prefs.setBoolPref("zen.swipe.is-fast-swipe", true);
|
||||
}
|
||||
|
||||
_handleSwipeUpdate(event) {
|
||||
const ws = gZenWorkspaces;
|
||||
|
||||
if (!ws.workspaceEnabled || !this._swipeState?.isGestureActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const delta = event.delta * 300;
|
||||
const stripWidth =
|
||||
window.windowUtils.getBoundsWithoutFlushing(
|
||||
document.getElementById("navigator-toolbox")
|
||||
).width +
|
||||
window.windowUtils.getBoundsWithoutFlushing(
|
||||
document.getElementById("zen-sidebar-splitter")
|
||||
).width *
|
||||
2;
|
||||
let translateX = this._swipeState.lastDelta + delta;
|
||||
// Add a force multiplier as we are translating the strip depending on how close to the edge we are
|
||||
let forceMultiplier = Math.min(
|
||||
1,
|
||||
1 - Math.abs(translateX) / (stripWidth * 4.5)
|
||||
); // 4.5 instead of 4 to add a bit of a buffer
|
||||
if (forceMultiplier > 0.5) {
|
||||
translateX *= forceMultiplier;
|
||||
this._swipeState.lastDelta = delta + (translateX - delta) * 0.5;
|
||||
} else {
|
||||
translateX = this._swipeState.lastDelta;
|
||||
}
|
||||
|
||||
if (Math.abs(delta) > 0.8) {
|
||||
this._swipeState.direction = delta > 0 ? "left" : "right";
|
||||
}
|
||||
|
||||
// Apply a translateX to the tab strip to give the user feedback on the swipe
|
||||
const currentWorkspace = ws.getActiveWorkspaceFromCache();
|
||||
ws._organizeWorkspaceStripLocations(currentWorkspace, true, translateX);
|
||||
}
|
||||
|
||||
async _handleSwipeEnd(event) {
|
||||
const ws = gZenWorkspaces;
|
||||
|
||||
if (!ws.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const isRTL = document.documentElement.matches(":-moz-locale-dir(rtl)");
|
||||
const moveForward =
|
||||
(event.direction === SimpleGestureEvent.DIRECTION_RIGHT) !== isRTL;
|
||||
|
||||
const rawDirection = moveForward ? 1 : -1;
|
||||
const direction = ws.naturalScroll ? -1 : 1;
|
||||
await ws.changeWorkspaceShortcut(rawDirection * direction, true);
|
||||
|
||||
// Reset swipe state
|
||||
this._swipeState = {
|
||||
isGestureActive: false,
|
||||
lastDelta: 0,
|
||||
direction: null,
|
||||
};
|
||||
}
|
||||
|
||||
onSwipeGestureAnimationEnd() {
|
||||
const ws = gZenWorkspaces;
|
||||
|
||||
Services.prefs.setBoolPref("zen.swipe.is-fast-swipe", false);
|
||||
document.documentElement.removeAttribute("swipe-gesture");
|
||||
gZenUIManager.tabsWrapper.style.removeProperty("scrollbar-width");
|
||||
[lazy.browserBackgroundElement, lazy.toolbarBackgroundElement].forEach(
|
||||
element => {
|
||||
element.style.setProperty("--zen-background-opacity", "1");
|
||||
}
|
||||
);
|
||||
delete ws._hasAnimatedBackgrounds;
|
||||
ws.updateTabsContainers();
|
||||
document.removeEventListener("popupshown", ws.popupOpenHandler, {
|
||||
once: true,
|
||||
});
|
||||
}
|
||||
|
||||
get isGestureActive() {
|
||||
return this._swipeState?.isGestureActive;
|
||||
}
|
||||
}
|
||||
7
src/zen/spaces/jar.inc.mn
Normal file
7
src/zen/spaces/jar.inc.mn
Normal file
@@ -0,0 +1,7 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
content/browser/zen-components/ZenSpaceBookmarksStorage.js (../../zen/spaces/ZenSpaceBookmarksStorage.js)
|
||||
* content/browser/zen-styles/zen-workspaces.css (../../zen/spaces/zen-workspaces.css)
|
||||
content/browser/zen-styles/zen-gradient-generator.css (../../zen/spaces/zen-gradient-generator.css)
|
||||
12
src/zen/spaces/moz.build
Normal file
12
src/zen/spaces/moz.build
Normal file
@@ -0,0 +1,12 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES.zen += [
|
||||
"ZenGradientGenerator.mjs",
|
||||
"ZenSpace.mjs",
|
||||
"ZenSpaceCreation.mjs",
|
||||
"ZenSpaceIcons.mjs",
|
||||
"ZenSpaceManager.mjs",
|
||||
"ZenSpacesSwipe.mjs",
|
||||
]
|
||||
@@ -274,7 +274,7 @@
|
||||
&:first-of-type {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-width: 4px;
|
||||
border-width: 6px;
|
||||
pointer-events: all;
|
||||
transition: transform 0.2s;
|
||||
z-index: 999;
|
||||
@@ -13,7 +13,6 @@
|
||||
font-size: x-small;
|
||||
margin: 0 3px;
|
||||
padding: 0;
|
||||
appearance: auto;
|
||||
|
||||
position: relative;
|
||||
-moz-window-dragging: no-drag;
|
||||
@@ -243,7 +242,6 @@
|
||||
|
||||
.zen-current-workspace-indicator-name {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: auto;
|
||||
@@ -2,5 +2,5 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
* content/browser/zen-styles/zen-decks.css (../../zen/split-view/zen-decks.css)
|
||||
* content/browser/zen-styles/zen-split-view.css (../../zen/split-view/zen-split-view.css)
|
||||
content/browser/zen-components/ZenViewSplitter.mjs (../../zen/split-view/ZenViewSplitter.mjs)
|
||||
|
||||
@@ -38,6 +38,16 @@
|
||||
/* Fix for issue https://github.com/zen-browser/desktop/issues/7564 */
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
:root[inDOMFullscreen="true"] & {
|
||||
&:not(.deck-selected) {
|
||||
-moz-subtree-hidden-only-visually: 1 !important;
|
||||
}
|
||||
|
||||
&.deck-selected {
|
||||
inset: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.browserSidebarContainer[is-zen-split="true"],
|
||||
@@ -59,7 +69,7 @@
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view="true"]:not(.zen-split-view-no-transition):not([zen-split-resizing]) > [zen-split="true"] {
|
||||
--zen-active-split-outline-color: light-dark(var(--zen-primary-color), var(--button-background-color-primary));
|
||||
--zen-active-split-outline-color: light-dark(hsl(from var(--zen-primary-color) h s calc(l - 20)), var(--button-background-color-primary));
|
||||
|
||||
transition: inset 0.09s ease-out !important;
|
||||
& browser {
|
||||
@@ -67,7 +77,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
#tabbrowser-tabpanels[zen-split-view="true"] .browserSidebarContainer.deck-selected {
|
||||
:root:not([inDOMFullscreen="true"]) #tabbrowser-tabpanels[zen-split-view="true"] .browserSidebarContainer.deck-selected {
|
||||
&:not(.zen-glance-overlay) {
|
||||
outline: 2px solid var(--zen-active-split-outline-color) !important;
|
||||
}
|
||||
@@ -163,7 +173,8 @@
|
||||
display: flex;
|
||||
-moz-context-properties: fill, fill-opacity;
|
||||
border-radius: var(--tab-border-radius);
|
||||
color: inherit;
|
||||
color: contrast-color(var(--zen-active-split-outline-color));
|
||||
opacity: 0.8;
|
||||
fill: currentColor;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
@@ -115,7 +115,18 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
|
||||
_onTabResetPinButton(event, tab) {
|
||||
event.stopPropagation();
|
||||
this._resetTabToStoredState(tab);
|
||||
if (event.getModifierState("Accel")) {
|
||||
let newTab = gBrowser.duplicateTab(tab, true);
|
||||
newTab.addEventListener(
|
||||
"SSTabRestored",
|
||||
() => {
|
||||
this._resetTabToStoredState(tab);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
} else {
|
||||
this._resetTabToStoredState(tab);
|
||||
}
|
||||
gBrowser.selectedTab = tab;
|
||||
}
|
||||
|
||||
@@ -170,6 +181,38 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
_onAccelKeyChange(e) {
|
||||
let tab = this._tabWithResetPinButtonHovered;
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
let accelHeld =
|
||||
e.getModifierState("Accel") || (e.metaKey && e.type == "keydown");
|
||||
this._setResetPinSublabel(tab, accelHeld);
|
||||
// Up <-> down events until the mouse leaves the button.
|
||||
// When hovered with accelHeld, we should listen to the next keyup event
|
||||
let nextEvent = accelHeld ? "keyup" : "keydown";
|
||||
let handler = nextE => this._onAccelKeyChange(nextE);
|
||||
window.addEventListener(nextEvent, handler, { once: true });
|
||||
}
|
||||
|
||||
_setResetPinSublabel(tab, accelHeld) {
|
||||
let label = tab.querySelector(".zen-tab-sublabel");
|
||||
document.l10n.setArgs(label, {
|
||||
tabSubtitle: accelHeld ? "zen-default-pinned-cmd" : "zen-default-pinned",
|
||||
});
|
||||
}
|
||||
|
||||
onResetPinButtonMouseOver(tab, event) {
|
||||
this._tabWithResetPinButtonHovered = tab;
|
||||
this._onAccelKeyChange(event);
|
||||
}
|
||||
|
||||
onResetPinButtonMouseOut(tab) {
|
||||
this._setResetPinSublabel(tab, false);
|
||||
delete this._tabWithResetPinButtonHovered;
|
||||
}
|
||||
|
||||
resetPinnedTab(tab) {
|
||||
if (!tab) {
|
||||
tab = TabContextMenu.contextTab;
|
||||
@@ -775,8 +818,15 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
onLocationChange(browser) {
|
||||
const tab = gBrowser.getTabForBrowser(browser);
|
||||
onLocationChange(aBrowser, aLocation) {
|
||||
if (
|
||||
(aLocation == "about:blank" &&
|
||||
BrowserUIUtils.checkEmptyPageOrigin(aBrowser)) ||
|
||||
aLocation == ""
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const tab = gBrowser.getTabForBrowser(aBrowser);
|
||||
if (
|
||||
!tab ||
|
||||
!tab.pinned ||
|
||||
@@ -787,7 +837,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
// Remove # and ? from the URL
|
||||
const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0];
|
||||
const currentUrl = browser.currentURI.spec.split("#")[0];
|
||||
const currentUrl = aLocation.split("#")[0];
|
||||
// Add an indicator that the pin has been changed
|
||||
if (pinUrl === currentUrl) {
|
||||
this.resetPinChangedUrl(tab);
|
||||
@@ -812,7 +862,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
tab.removeAttribute("zen-show-sublabel");
|
||||
|
||||
const label = tab.querySelector(".zen-tab-sublabel");
|
||||
window.document.l10n.setArgs(label, {
|
||||
document.l10n.setArgs(label, {
|
||||
tabSubtitle: "zen-default-pinned",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -325,11 +325,15 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tabbrowser-tabs:not([movingtab]) &:active:not(:has(.tab-content > image:active)) {
|
||||
:root:not([zen-renaming-tab="true"])
|
||||
#tabbrowser-tabs:not([movingtab])
|
||||
&:active:not(:has(.tab-content > image:active)) {
|
||||
scale: var(--zen-active-tab-scale);
|
||||
}
|
||||
|
||||
#tabbrowser-tabs:not([movingtab]) & .tab-content > image:active {
|
||||
:root:not([zen-renaming-tab="true"])
|
||||
#tabbrowser-tabs:not([movingtab])
|
||||
& .tab-content > image:active {
|
||||
scale: 0.92;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ prefs = ["zen.workspaces.separate-essentials=false"]
|
||||
|
||||
["browser_pinned_nounload_reset.js"]
|
||||
|
||||
["browser_pinned_reset_button.js"]
|
||||
|
||||
["browser_pinned_reset_noswitch.js"]
|
||||
|
||||
["browser_pinned_switch.js"]
|
||||
|
||||
199
src/zen/tests/pinned/browser_pinned_reset_button.js
Normal file
199
src/zen/tests/pinned/browser_pinned_reset_button.js
Normal file
@@ -0,0 +1,199 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function pinAndNavigateTab(url, navigateTo) {
|
||||
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
gBrowser.pinTab(tab);
|
||||
await gBrowser.TabStateFlusher.flush(tab.linkedBrowser);
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
|
||||
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, navigateTo);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, navigateTo);
|
||||
return tab;
|
||||
}
|
||||
|
||||
add_task(async function test_ResetPinButton_SelectsTab() {
|
||||
const tab = await pinAndNavigateTab(
|
||||
"https://example.com/1",
|
||||
"https://example.com/2"
|
||||
);
|
||||
|
||||
// Open another tab and select it
|
||||
const otherTab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
"https://example.com/other"
|
||||
);
|
||||
Assert.notEqual(
|
||||
gBrowser.selectedTab,
|
||||
tab,
|
||||
"The pinned tab should not be selected initially"
|
||||
);
|
||||
|
||||
// Simulate clicking the reset pin button (without Accel key)
|
||||
gZenPinnedTabManager._onTabResetPinButton(
|
||||
{
|
||||
stopPropagation() {},
|
||||
getModifierState() {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
tab
|
||||
);
|
||||
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
|
||||
Assert.strictEqual(
|
||||
gBrowser.selectedTab,
|
||||
tab,
|
||||
"The pinned tab should be selected after reset"
|
||||
);
|
||||
ok(
|
||||
!tab.hasAttribute("zen-pinned-changed"),
|
||||
"zen-pinned-changed should be removed after reset"
|
||||
);
|
||||
|
||||
gBrowser.removeTab(otherTab);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_ResetPinButton_CmdClick_DuplicatesAndResets() {
|
||||
const originalUrl = "https://example.com/1";
|
||||
const navigatedUrl = "https://example.com/2";
|
||||
const tab = await pinAndNavigateTab(originalUrl, navigatedUrl);
|
||||
const tabCountBefore = gBrowser.tabs.length;
|
||||
|
||||
// Simulate CMD+click on the reset pin button
|
||||
gZenPinnedTabManager._onTabResetPinButton(
|
||||
{
|
||||
stopPropagation() {},
|
||||
getModifierState() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
tab
|
||||
);
|
||||
|
||||
// Wait for the duplicate tab to be restored
|
||||
const restoredEvent = await BrowserTestUtils.waitForEvent(
|
||||
gBrowser.tabContainer,
|
||||
"SSTabRestored"
|
||||
);
|
||||
const newTab = restoredEvent.target;
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
|
||||
Assert.equal(
|
||||
gBrowser.tabs.length,
|
||||
tabCountBefore + 1,
|
||||
"A new tab should be created from the duplicate"
|
||||
);
|
||||
Assert.equal(
|
||||
newTab.linkedBrowser.currentURI.spec,
|
||||
navigatedUrl,
|
||||
"The duplicated tab should have the navigated URL"
|
||||
);
|
||||
ok(!newTab.pinned, "The duplicated tab should not be pinned");
|
||||
|
||||
Assert.strictEqual(
|
||||
gBrowser.selectedTab,
|
||||
tab,
|
||||
"The pinned tab should be selected after CMD+click reset"
|
||||
);
|
||||
ok(
|
||||
!tab.hasAttribute("zen-pinned-changed"),
|
||||
"zen-pinned-changed should be removed after reset"
|
||||
);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
originalUrl,
|
||||
"The pinned tab should be reset to the original URL"
|
||||
);
|
||||
|
||||
gBrowser.removeTab(newTab);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_Hover_SublabelChangesWithAccelKey() {
|
||||
const tab = await pinAndNavigateTab(
|
||||
"https://example.com/1",
|
||||
"https://example.com/2"
|
||||
);
|
||||
|
||||
// Track calls to document.l10n.setArgs to verify sublabel updates
|
||||
const sublabelArgs = [];
|
||||
const label = tab.querySelector(".zen-tab-sublabel");
|
||||
const origSetArgs = document.l10n.setArgs;
|
||||
document.l10n.setArgs = (el, args) => {
|
||||
if (el === label) {
|
||||
sublabelArgs.push(args.tabSubtitle);
|
||||
}
|
||||
origSetArgs.call(document.l10n, el, args);
|
||||
};
|
||||
|
||||
try {
|
||||
// Simulate hovering with no modifier key held
|
||||
gZenPinnedTabManager.onResetPinButtonMouseOver(tab, {
|
||||
getModifierState() {
|
||||
return false;
|
||||
},
|
||||
metaKey: false,
|
||||
type: "mouseover",
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
sublabelArgs.at(-1),
|
||||
"zen-default-pinned",
|
||||
"Sublabel should show default text on hover without Accel"
|
||||
);
|
||||
|
||||
// Simulate pressing CMD while hovering
|
||||
gZenPinnedTabManager._onAccelKeyChange({
|
||||
getModifierState() {
|
||||
return true;
|
||||
},
|
||||
metaKey: true,
|
||||
type: "keydown",
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
sublabelArgs.at(-1),
|
||||
"zen-default-pinned-cmd",
|
||||
"Sublabel should show CMD text when Accel key is pressed"
|
||||
);
|
||||
|
||||
// Simulate releasing CMD while still hovering
|
||||
gZenPinnedTabManager._onAccelKeyChange({
|
||||
getModifierState() {
|
||||
return false;
|
||||
},
|
||||
metaKey: false,
|
||||
type: "keyup",
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
sublabelArgs.at(-1),
|
||||
"zen-default-pinned",
|
||||
"Sublabel should revert to default text when Accel key is released"
|
||||
);
|
||||
|
||||
// Simulate mouse out
|
||||
gZenPinnedTabManager.onResetPinButtonMouseOut(tab);
|
||||
|
||||
Assert.equal(
|
||||
sublabelArgs.at(-1),
|
||||
"zen-default-pinned",
|
||||
"Sublabel should show default text after mouse out"
|
||||
);
|
||||
ok(
|
||||
!gZenPinnedTabManager._tabWithResetPinButtonHovered,
|
||||
"Hovered tab reference should be cleared after mouse out"
|
||||
);
|
||||
} finally {
|
||||
document.l10n.setArgs = origSetArgs;
|
||||
}
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
||||
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
||||
content/browser/zen-components/ZenWorkspaces.mjs (../../zen/workspaces/ZenWorkspaces.mjs)
|
||||
content/browser/zen-components/ZenWorkspaceCreation.mjs (../../zen/workspaces/ZenWorkspaceCreation.mjs)
|
||||
content/browser/zen-components/ZenWorkspaceBookmarksStorage.js (../../zen/workspaces/ZenWorkspaceBookmarksStorage.js)
|
||||
content/browser/zen-components/ZenGradientGenerator.mjs (../../zen/workspaces/ZenGradientGenerator.mjs)
|
||||
* content/browser/zen-styles/zen-workspaces.css (../../zen/workspaces/zen-workspaces.css)
|
||||
content/browser/zen-styles/zen-gradient-generator.css (../../zen/workspaces/zen-gradient-generator.css)
|
||||
@@ -20,7 +20,7 @@
|
||||
"brandShortName": "Zen",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.19.2b",
|
||||
"displayVersion": "1.19.3b",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user