chore: Refactor spaces organization, p=#12764

This commit is contained in:
mr. m
2026-03-13 17:35:00 +01:00
committed by GitHub
parent 420d4ec064
commit 6287f1a118
22 changed files with 249 additions and 205 deletions

View File

@@ -1,5 +1,5 @@
diff --git a/.stylelintignore b/.stylelintignore
index 185490999507b8a5032977237af50f5e61c71df1..e887fafa90b881e852a287ed8898638c995861ab 100644
index 185490999507b8a5032977237af50f5e61c71df1..d535178552c6a744022be1d0bfff5fbb8bf9a5ef 100644
--- a/.stylelintignore
+++ b/.stylelintignore
@@ -106,3 +106,19 @@ build/pgo/blueprint/**/*.css
@@ -18,7 +18,7 @@ index 185490999507b8a5032977237af50f5e61c71df1..e887fafa90b881e852a287ed8898638c
+zen/compact-mode/zen-compact-mode.css
+
+zen/split-view/zen-decks.css
+zen/workspaces/zen-workspaces.css
+zen/spaces/zen-workspaces.css
+zen/common/styles/zen-toolbar.css
+
+*.inc

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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"/>

View File

@@ -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;
}

View File

@@ -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" });

View File

@@ -17,4 +17,5 @@ DIRS += [
"toolkit",
"sessionstore",
"share",
"spaces",
]

View File

@@ -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

View 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;
}
}

View 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
View 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",
]

View File

@@ -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;

View File

@@ -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)