gh-13870: Fix essentials overlap and fix two space wrap-around (gh-14476)

This commit is contained in:
Andrey Bochkarev
2026-07-05 13:45:35 +03:00
committed by GitHub
parent 79d3d6a993
commit 3a72290f16
2 changed files with 56 additions and 196 deletions

View File

@@ -449,7 +449,7 @@ class nsZenWorkspaces {
container = 0;
}
let essentialsContainer = document.querySelector(
`.zen-essentials-container[container="${container}"]:not([cloned])`
`.zen-essentials-container[container="${container}"]`
);
if (!essentialsContainer) {
essentialsContainer = document.createXULElement("hbox");
@@ -1864,6 +1864,11 @@ class nsZenWorkspaces {
}
const newTransform = diff * 100;
element.style.transform = `translateX(${newTransform + offsetPixels / 2}%)`;
// A special case for two spaces
if (spaceLen === 2 && diff !== 0) {
const side = offsetPixels > 0 ? -100 : 100;
element.style.transform = `translateX(${side + offsetPixels / 2}%)`;
}
}
// Hide other essentials with different containerTabId
for (const container of otherContainersEssentials) {
@@ -2017,28 +2022,20 @@ class nsZenWorkspaces {
diff += spaceLen;
}
const isGoingLeft = diff < 0;
const clonedEssentials = [];
const essentialsAnimData = [];
if (shouldAnimate && this.shouldAnimateEssentials && previousWorkspace) {
const containerIds = new Map();
for (const workspace of workspaces) {
const essentialsContainer = this.getEssentialsSection(
workspace.containerTabId
);
let lastCloned = clonedEssentials[clonedEssentials.length - 1];
if (lastCloned && lastCloned.contextId == workspace.containerTabId) {
lastCloned.repeat++;
lastCloned.workspaces.push(workspace);
continue;
const containerId = workspace.containerTabId;
if (!containerIds.has(containerId)) {
containerIds.set(containerId, []);
}
essentialsContainer.setAttribute("hidden", "true");
const essentialsClone = essentialsContainer.cloneNode(true);
essentialsClone.removeAttribute("hidden");
essentialsClone.setAttribute("cloned", "true");
clonedEssentials.push({
container: essentialsClone,
workspaces: [workspace],
contextId: workspace.containerTabId,
originalContainer: essentialsContainer,
repeat: 0,
containerIds.get(containerId).push(workspace);
}
for (const [containerId, spaces] of containerIds) {
essentialsAnimData.push({
element: this.getEssentialsSection(containerId),
workspaces: spaces,
});
}
}
@@ -2102,6 +2099,12 @@ class nsZenWorkspaces {
offset += spaceLen;
}
offset = offset * 100;
// A special case for two spaces
if (spaceLen === 2 && offset !== 0) {
const currentTransform =
parseFloat(element.style.transform.split("(")[1]) || 0;
offset = currentTransform >= 0 ? 100 : -100;
}
const newTransform = `translateX(${offset}%)`;
// Only animate the workspace that is coming in, to avoid having multiple workspaces
// animating off-screen at the same time which can cause performance issues. With an off
@@ -2145,190 +2148,59 @@ class nsZenWorkspaces {
}
}
if (this.shouldAnimateEssentials && previousWorkspace) {
// Animate essentials
const newWorkspaceEssentialsContainer = clonedEssentials.find(cloned =>
cloned.workspaces.some(w => w.uuid === newWorkspace.uuid)
);
// Get a list of essentials containers that are in between the first and last workspace
const essentialsContainersInBetween = clonedEssentials.filter(cloned => {
const essentialsWorkspaces = cloned.workspaces;
const firstIndex = workspaces.findIndex(
w => w.uuid === essentialsWorkspaces[0].uuid
for (const data of essentialsAnimData) {
const container = data.element;
const essentialsWorkspaces = data.workspaces;
const containsPrev = essentialsWorkspaces.some(
w => w.uuid === previousWorkspace.uuid
);
const lastIndex = workspaces.findIndex(
w =>
w.uuid ===
essentialsWorkspaces[essentialsWorkspaces.length - 1].uuid
const containsNew = essentialsWorkspaces.some(
w => w.uuid === newWorkspace.uuid
);
const [start, end] = [
Math.min(previousWorkspaceIndex, newWorkspaceIndex),
Math.max(previousWorkspaceIndex, newWorkspaceIndex),
];
// Check if any part of the container overlaps with the movement range
return firstIndex <= end && lastIndex >= start;
});
for (const cloned of clonedEssentials) {
const container = cloned.container;
const essentialsWorkspaces = cloned.workspaces;
const repeats = cloned.repeat;
// Animate like the workspaces above expect essentials are a bit more
// complicated because they are not based on workspaces but on containers
// So, if we have the following arangement:
// | [workspace1] [workspace2] [workspace3] [workspace4]
// | [container1] [container1] [container2] [container1]
// And if we are changing from workspace 1 to workspace 4,
// we should be doing the following:
// First container (repeat 2 times) will stay in place until
// we reach container 3, then animate to the left and container 2
// also move to the left after that while container 1 in workspace 4
// will slide in from the right
// Get the index from first and last workspace
const firstWorkspaceIndex = workspaces.findIndex(
w => w.uuid === essentialsWorkspaces[0].uuid
);
const lastWorkspaceIndex = workspaces.findIndex(
w =>
w.uuid ===
essentialsWorkspaces[essentialsWorkspaces.length - 1].uuid
);
cloned.originalContainer.style.removeProperty("transform");
// Check if the container is even going to appear on the screen, to save on animation
if (
// We also need to check if the container is even going to appear on the screen.
// In order to do this, we need to check if the container is between the first and last workspace.
// Note that essential containers can have multiple workspaces,
// so we need to check if any of the workspaces in the container are between the
// first and last workspace.
!essentialsContainersInBetween.find(
ce =>
ce.workspaces.some(
w => w.uuid === essentialsWorkspaces[0].uuid
) &&
ce.workspaces.some(
w =>
w.uuid ===
essentialsWorkspaces[essentialsWorkspaces.length - 1].uuid
)
)
) {
if (!containsPrev && !containsNew) {
container.setAttribute("hidden", "true");
continue;
}
cloned.originalContainer.parentNode.appendChild(container);
let stepsInBetween =
Math.abs(
newWorkspaceIndex -
(isGoingLeft ? firstWorkspaceIndex : lastWorkspaceIndex)
) + 1;
const usingSameContainer =
newWorkspaceEssentialsContainer?.workspaces.some(
w => w.uuid === newWorkspace.uuid
) &&
newWorkspaceEssentialsContainer?.workspaces.some(
w => w.uuid === previousWorkspace.uuid
);
let newOffset =
-(
newWorkspaceIndex -
(isGoingLeft ? firstWorkspaceIndex : lastWorkspaceIndex) +
(!isGoingLeft ? repeats - 1 : -repeats + 1)
) * 100;
let existingOffset =
-(
newWorkspaceIndex -
(isGoingLeft ? lastWorkspaceIndex : firstWorkspaceIndex) +
(isGoingLeft ? repeats - 1 : -repeats + 1)
) * 100;
container.removeAttribute("hidden");
// If we are on the same container and both new and old workspace are in the same "essentialsWorkspaces"
// we can simply not animate the essentials
if (
usingSameContainer &&
essentialsWorkspaces.some(w => w.uuid === newWorkspace.uuid) &&
essentialsWorkspaces.some(w => w.uuid === previousWorkspace.uuid)
) {
newOffset = 0;
existingOffset = 0;
if (containsPrev && containsNew) {
container.style.transform = "translateX(0%)";
continue;
}
const needsOffsetAdjustment =
stepsInBetween > essentialsWorkspaces.length || usingSameContainer;
let existingOffset, newOffset;
const currentTransform =
parseFloat(container.style.transform.split("(")[1]) || 0;
if (containsPrev && !containsNew) {
existingOffset = currentTransform;
newOffset = isGoingLeft ? 100 : -100;
} else {
existingOffset = currentTransform || (isGoingLeft ? -100 : 100);
newOffset = 0;
}
if (repeats > 0 && needsOffsetAdjustment) {
if (!isGoingLeft) {
if (existingOffset !== 0) {
existingOffset += 100;
}
if (newOffset !== 0) {
newOffset += 100;
}
if (spaceLen === 2) {
if (containsPrev && !containsNew) {
existingOffset = currentTransform;
newOffset = currentTransform >= 0 ? 100 : -100;
} else {
if (existingOffset !== 0) {
existingOffset -= 100;
}
if (newOffset !== 0) {
newOffset -= 100;
}
existingOffset = currentTransform >= 0 ? 100 : -100;
newOffset = 0;
}
}
// Special case: going forward from single reused container to a new one
if (
!usingSameContainer &&
!isGoingLeft &&
lastWorkspaceIndex === newWorkspaceIndex - 1
) {
existingOffset = 0;
newOffset = -100;
stepsInBetween = 1;
}
if (
!usingSameContainer &&
isGoingLeft &&
firstWorkspaceIndex === newWorkspaceIndex + 1
) {
existingOffset = 0;
newOffset = 100;
stepsInBetween = 1;
}
if (
!usingSameContainer &&
isGoingLeft &&
(firstWorkspaceIndex === newWorkspaceIndex - 1 ||
firstWorkspaceIndex === newWorkspaceIndex)
) {
existingOffset = -100;
newOffset = 0;
stepsInBetween = 1;
}
if (
!usingSameContainer &&
!isGoingLeft &&
firstWorkspaceIndex === newWorkspaceIndex
) {
existingOffset = 100;
newOffset = 0;
stepsInBetween = 1;
}
const newTransform = `translateX(${newOffset}%)`;
let existingTransform = `translateX(${existingOffset}%)`;
if (container.style.transform && container.style.transform !== "none") {
existingTransform = container.style.transform;
}
if (shouldAnimate) {
container.style.transform = existingTransform;
animations.push(
gZenUIManager.motion.animate(
container,
{
transform: [
existingTransform,
new Array(stepsInBetween).fill(newTransform).join(","),
],
transform: [existingTransform, newTransform],
},
{
type: "spring",
@@ -2357,17 +2229,12 @@ class nsZenWorkspaces {
this.#currentSpaceSwitchContext.animations = [];
document.documentElement.removeAttribute("animating-background");
if (shouldAnimate) {
for (const cloned of clonedEssentials) {
cloned.container.remove();
for (const data of essentialsAnimData) {
data.element.style.removeProperty("transform");
}
this._alwaysAnimatePaddingTop = true;
this.updateTabsContainers();
}
const essentialsContainer = this.getEssentialsSection(
newWorkspace.containerTabId
);
essentialsContainer.removeAttribute("hidden");
essentialsContainer.style.transform = "none";
gBrowser.tabContainer._invalidateCachedTabs();
gZenUIManager.tabsWrapper.style.removeProperty("scrollbar-width");
this._animatingChange = false;
@@ -3242,9 +3109,6 @@ class nsZenWorkspaces {
...normalContainers,
];
for (const container of containers) {
if (container.hasAttribute("cloned")) {
continue;
}
for (const tab of container.children) {
if (gBrowser.isTab(tab)) {
tabs.push(tab);

View File

@@ -1156,10 +1156,6 @@
pointer-events: none;
position: fixed; /* Fix position to prevent scrolling */
}
&[cloned] {
pointer-events: none;
}
}
.tabbrowser-tab[zen-essential="true"],