mirror of
https://github.com/zen-browser/desktop.git
synced 2025-12-31 18:52:15 +00:00
Merge branch 'dev' of https://github.com/zen-browser/desktop into dev
This commit is contained in:
@@ -40,12 +40,6 @@
|
||||
- name: zen.view.window.scheme
|
||||
value: 2
|
||||
|
||||
- name: zen.view.drag-and-drop.move-over-threshold
|
||||
value: 70
|
||||
|
||||
- name: zen.view.drag-and-drop.edge-zone-threshold
|
||||
value: 25
|
||||
|
||||
- name: zen.view.context-menu.refresh
|
||||
value: '@IS_TWILIGHT@'
|
||||
|
||||
|
||||
@@ -20,6 +20,15 @@
|
||||
- name: zen.tabs.close-window-with-empty
|
||||
value: true
|
||||
|
||||
- name: zen.tabs.use-legacy-drag-and-drop
|
||||
value: false
|
||||
|
||||
- name: zen.tabs.folder-dragover-threshold-percent
|
||||
value: 20 # Percentage of folder height to trigger dragover
|
||||
|
||||
- name: zen.tabs.dnd-switch-space-delay
|
||||
value: 1000 # milliseconds
|
||||
|
||||
- name: zen.ctrlTab.show-pending-tabs
|
||||
value: false
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include ../../../zen/common/jar.inc.mn
|
||||
#include ../../../zen/compact-mode/jar.inc.mn
|
||||
#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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..4439fe5fb3c7002b173415b615892ef356b22959 100644
|
||||
index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..697dde4378c43ae6db46a6b7eb2997982201ec27 100644
|
||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
@@ -127,6 +127,8 @@ const TAB_EVENTS = [
|
||||
@@ -177,7 +177,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..4439fe5fb3c7002b173415b615892ef3
|
||||
|
||||
+ winData.folders = aWindow.gZenFolders?.storeDataForSessionStore() || [];
|
||||
+ winData.activeZenSpace = aWindow.gZenWorkspaces?.activeWorkspace || null;
|
||||
+ winData.spaces = aWindow.gZenWorkspaces?.getWorkspaces();
|
||||
+ winData.spaces = aWindow.gZenWorkspaces?.getWorkspacesForSessionStore();
|
||||
// update tab group state for this window
|
||||
winData.groups = [];
|
||||
for (let tabGroup of aWindow.gBrowser.tabGroups) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js
|
||||
index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9548dce83 100644
|
||||
index 97b931c3c7385a52d20204369fcf6d6999053687..6a136cf14d0bc081507a05f298f12ac7a7914601 100644
|
||||
--- a/browser/components/tabbrowser/content/drag-and-drop.js
|
||||
+++ b/browser/components/tabbrowser/content/drag-and-drop.js
|
||||
@@ -32,6 +32,9 @@
|
||||
@@ -12,26 +12,33 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
if (isTab(element)) {
|
||||
return element;
|
||||
}
|
||||
@@ -112,6 +115,10 @@
|
||||
@@ -112,6 +115,9 @@
|
||||
}
|
||||
|
||||
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
+ if (draggedTab && dropEffect === "move") {
|
||||
+ gZenPinnedTabManager.applyDragoverClass(event, draggedTab);
|
||||
+ gZenViewSplitter.onBrowserDragEndToSplit(event);
|
||||
+ }
|
||||
if (
|
||||
(dropEffect == "move" || dropEffect == "copy") &&
|
||||
document == draggedTab.ownerDocument &&
|
||||
@@ -266,6 +273,18 @@
|
||||
@@ -130,10 +136,6 @@
|
||||
|
||||
// Pinned tabs in expanded vertical mode are on a grid format and require
|
||||
// different logic to drag and drop.
|
||||
- if (this._isContainerVerticalPinnedGrid(draggedTab)) {
|
||||
- this._animateExpandedPinnedTabMove(event);
|
||||
- return;
|
||||
- }
|
||||
this._animateTabMove(event);
|
||||
return;
|
||||
}
|
||||
@@ -266,6 +268,15 @@
|
||||
|
||||
this._tabDropIndicator.hidden = true;
|
||||
event.stopPropagation();
|
||||
+ if (draggedTab?.hasAttribute("zen-has-splitted")) {
|
||||
+ draggedTab.removeAttribute("zen-has-splitted");
|
||||
+ draggedTab._visuallySelected = false;
|
||||
+ }
|
||||
+ if (draggedTab && dropEffect == "move") {
|
||||
+ this.handle_drop_transition?.(draggedTab._dragData.dropElement, draggedTab, movingTabs, draggedTab._dragData.dropBefore);
|
||||
+ let moved = gZenPinnedTabManager.moveToAnotherTabContainerIfNecessary(event, movingTabs);
|
||||
+
|
||||
+ if (moved) {
|
||||
@@ -42,7 +49,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
if (draggedTab && dropEffect == "copy") {
|
||||
let duplicatedDraggedTab;
|
||||
let duplicatedTabs = [];
|
||||
@@ -291,8 +310,9 @@
|
||||
@@ -291,8 +302,9 @@
|
||||
let translateOffsetY = oldTranslateY % tabHeight;
|
||||
let newTranslateX = oldTranslateX - translateOffsetX;
|
||||
let newTranslateY = oldTranslateY - translateOffsetY;
|
||||
@@ -54,7 +61,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
|
||||
if (this._isContainerVerticalPinnedGrid(draggedTab)) {
|
||||
// Update both translate axis for pinned vertical expanded tabs
|
||||
@@ -308,8 +328,8 @@
|
||||
@@ -308,8 +320,8 @@
|
||||
}
|
||||
} else {
|
||||
let tabs = this._tabbrowserTabs.ariaFocusableItems.slice(
|
||||
@@ -65,7 +72,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
);
|
||||
let size = this._tabbrowserTabs.verticalMode ? "height" : "width";
|
||||
let screenAxis = this._tabbrowserTabs.verticalMode
|
||||
@@ -362,11 +382,13 @@
|
||||
@@ -362,11 +374,13 @@
|
||||
this._dragToPinPromoCard,
|
||||
];
|
||||
let shouldPin =
|
||||
@@ -79,7 +86,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
isTab(draggedTab) &&
|
||||
draggedTab.pinned &&
|
||||
this._tabbrowserTabs.arrowScrollbox.contains(event.target);
|
||||
@@ -384,6 +406,7 @@
|
||||
@@ -384,6 +398,7 @@
|
||||
(oldTranslateY && oldTranslateY != newTranslateY);
|
||||
} else if (this._tabbrowserTabs.verticalMode) {
|
||||
shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY;
|
||||
@@ -87,7 +94,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
} else {
|
||||
shouldTranslate &&= oldTranslateX && oldTranslateX != newTranslateX;
|
||||
}
|
||||
@@ -440,7 +463,7 @@
|
||||
@@ -440,7 +455,7 @@
|
||||
item.removeAttribute("tabdrop-samewindow");
|
||||
resolve();
|
||||
};
|
||||
@@ -96,7 +103,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
postTransitionCleanup();
|
||||
} else {
|
||||
let onTransitionEnd = transitionendEvent => {
|
||||
@@ -581,6 +604,7 @@
|
||||
@@ -581,6 +596,7 @@
|
||||
|
||||
let nextItem = this._tabbrowserTabs.ariaFocusableItems[newIndex];
|
||||
let tabGroup = isTab(nextItem) && nextItem.group;
|
||||
@@ -104,7 +111,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
gBrowser.loadTabs(urls, {
|
||||
inBackground,
|
||||
replace,
|
||||
@@ -618,7 +642,16 @@
|
||||
@@ -618,7 +634,16 @@
|
||||
this._expandGroupOnDrop(draggedTab);
|
||||
}
|
||||
this._resetTabsAfterDrop(draggedTab.ownerDocument);
|
||||
@@ -122,7 +129,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
if (
|
||||
dt.mozUserCancelled ||
|
||||
dt.dropEffect != "none" ||
|
||||
@@ -822,7 +855,10 @@
|
||||
@@ -822,7 +847,10 @@
|
||||
_getDragTarget(event, { ignoreSides = false } = {}) {
|
||||
let { target } = event;
|
||||
while (target) {
|
||||
@@ -134,7 +141,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
break;
|
||||
}
|
||||
target = target.parentNode;
|
||||
@@ -839,14 +875,17 @@
|
||||
@@ -839,14 +867,17 @@
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -154,7 +161,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
!this._tabbrowserTabs.expandOnHover
|
||||
);
|
||||
}
|
||||
@@ -877,7 +916,8 @@
|
||||
@@ -877,7 +908,8 @@
|
||||
isTabGroupLabel(draggedTab) &&
|
||||
draggedTab._dragData?.expandGroupOnDrop
|
||||
) {
|
||||
@@ -164,19 +171,23 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
}
|
||||
}
|
||||
|
||||
@@ -942,10 +982,7 @@
|
||||
if (this._isContainerVerticalPinnedGrid(tab)) {
|
||||
// In expanded vertical mode, the max number of pinned tabs per row is dynamic
|
||||
// Set this before adjusting dragged tab's position
|
||||
- let pinnedTabs = this._tabbrowserTabs.visibleTabs.slice(
|
||||
- 0,
|
||||
- gBrowser.pinnedTabCount
|
||||
- );
|
||||
+ let pinnedTabs = this._tabbrowserTabs.ariaFocusableItems.slice(0, gBrowser._numZenEssentials);
|
||||
let tabsPerRow = 0;
|
||||
let position = RTL_UI
|
||||
? window.windowUtils.getBoundsWithoutFlushing(
|
||||
@@ -1112,7 +1149,7 @@
|
||||
@@ -1055,7 +1087,6 @@
|
||||
// using updateDragImage. On Linux, we can use a panel.
|
||||
if (platform == "win" || platform == "macosx") {
|
||||
captureListener = function () {
|
||||
- dt.updateDragImage(canvas, dragImageOffset, dragImageOffset);
|
||||
};
|
||||
} else {
|
||||
// Create a panel to use it in setDragImage
|
||||
@@ -1093,7 +1124,6 @@
|
||||
);
|
||||
dragImageOffset = dragImageOffset * scale;
|
||||
}
|
||||
- dt.setDragImage(toDrag, dragImageOffset, dragImageOffset);
|
||||
|
||||
// _dragData.offsetX/Y give the coordinates that the mouse should be
|
||||
// positioned relative to the corner of the new window created upon
|
||||
@@ -1112,7 +1142,7 @@
|
||||
let dropEffect = this.getDropEffectForTabDrag(event);
|
||||
let isMovingInTabStrip = !fromTabList && dropEffect == "move";
|
||||
let collapseTabGroupDuringDrag =
|
||||
@@ -185,7 +196,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
|
||||
tab._dragData = {
|
||||
offsetX: this._tabbrowserTabs.verticalMode
|
||||
@@ -1122,7 +1159,7 @@
|
||||
@@ -1122,7 +1152,7 @@
|
||||
? event.screenY - window.screenY - tabOffset
|
||||
: event.screenY - window.screenY,
|
||||
scrollPos:
|
||||
@@ -194,7 +205,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition
|
||||
: this._tabbrowserTabs.arrowScrollbox.scrollPosition,
|
||||
screenX: event.screenX,
|
||||
@@ -1149,6 +1186,7 @@
|
||||
@@ -1149,6 +1179,7 @@
|
||||
|
||||
if (collapseTabGroupDuringDrag) {
|
||||
tab.group.collapsed = true;
|
||||
@@ -202,224 +213,15 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1173,6 +1211,16 @@
|
||||
@@ -1173,6 +1204,7 @@
|
||||
if (tabStripItemElement.hasAttribute("dragtarget")) {
|
||||
return;
|
||||
}
|
||||
+ let { movingTabs: zenMovingTabs } = tab._dragData;
|
||||
+ for (let movingTab of zenMovingTabs.slice(zenMovingTabs.findIndex(t => t._tPos == tab._tPos))) {
|
||||
+ if (isTabGroupLabel(tab)) {
|
||||
+ movingTab = movingTab.parentElement;
|
||||
+ }
|
||||
+ // "dragtarget" contains the following rules which must only be set AFTER the above
|
||||
+ // elements have been adjusted. {z-index: 3 !important, position: absolute !important}
|
||||
+ movingTab.setAttribute("zen-dragtarget", "");
|
||||
+ }
|
||||
+ return;
|
||||
let isPinned = tab.pinned;
|
||||
let numPinned = gBrowser.pinnedTabCount;
|
||||
let allTabs = this._tabbrowserTabs.ariaFocusableItems;
|
||||
@@ -1624,10 +1672,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
- let tabs = this._tabbrowserTabs.visibleTabs.slice(
|
||||
- 0,
|
||||
- gBrowser.pinnedTabCount
|
||||
- );
|
||||
+ let tabs = this._tabbrowserTabs.ariaFocusableItems.slice(0, gBrowser._numZenEssentials);
|
||||
|
||||
let directionX = screenX > dragData.animLastScreenX;
|
||||
let directionY = screenY > dragData.animLastScreenY;
|
||||
@@ -1636,6 +1681,8 @@
|
||||
|
||||
let { width: tabWidth, height: tabHeight } =
|
||||
draggedTab.getBoundingClientRect();
|
||||
+ tabWidth += 4; // Add 4px to account for the gap
|
||||
+ tabHeight += 4;
|
||||
let shiftSizeX = tabWidth * movingTabs.length;
|
||||
let shiftSizeY = tabHeight;
|
||||
dragData.tabWidth = tabWidth;
|
||||
@@ -1672,8 +1719,8 @@
|
||||
let lastBoundX =
|
||||
lastTabInRow.screenX +
|
||||
lastTabInRow.getBoundingClientRect().width -
|
||||
- (lastMovingTabScreenX + tabWidth);
|
||||
- let lastBoundY = periphery.screenY - (lastMovingTabScreenY + tabHeight);
|
||||
+ (lastMovingTabScreenX + tabWidth) + 4;
|
||||
+ let lastBoundY = lastTab.screenY - lastMovingTabScreenY;
|
||||
translateX = Math.min(Math.max(translateX, firstBoundX), lastBoundX);
|
||||
translateY = Math.min(Math.max(translateY, firstBoundY), lastBoundY);
|
||||
|
||||
@@ -1833,13 +1880,18 @@
|
||||
this._clearDragOverGroupingTimer();
|
||||
this.#clearPinnedDropIndicatorTimer();
|
||||
|
||||
- let isPinned = draggedTab.pinned;
|
||||
- let numPinned = gBrowser.pinnedTabCount;
|
||||
+ let isPinned = draggedTab?.group ? draggedTab.group.pinned : draggedTab.pinned;
|
||||
+ let numPinned = gBrowser._numVisiblePinTabsWithoutCollapsed;
|
||||
+ let essential = draggedTab.hasAttribute("zen-essential");
|
||||
+ const isDraggingFolder = isTabGroupLabel(draggedTab) && draggedTab.group?.isZenFolder;
|
||||
let allTabs = this._tabbrowserTabs.ariaFocusableItems;
|
||||
let tabs = allTabs.slice(
|
||||
- isPinned ? 0 : numPinned,
|
||||
- isPinned ? numPinned : undefined
|
||||
+ (isPinned && essential) ? 0 : gBrowser._numZenEssentials,
|
||||
+ isPinned ? (essential ? gBrowser._numZenEssentials : (isDraggingFolder ? numPinned : undefined)) : undefined
|
||||
);
|
||||
+ if (draggedTab.group?.hasAttribute("split-view-group")) {
|
||||
+ draggedTab = draggedTab.group.labelElement;
|
||||
+ }
|
||||
|
||||
if (this._rtlMode) {
|
||||
tabs.reverse();
|
||||
@@ -1854,7 +1906,7 @@
|
||||
let translateAxis = this._tabbrowserTabs.verticalMode
|
||||
? "translateY"
|
||||
: "translateX";
|
||||
- let { width: tabWidth, height: tabHeight } = bounds(draggedTab);
|
||||
+ let { width: tabWidth, height: tabHeight } = bounds(draggedTab.group?.hasAttribute("split-view-group") ? draggedTab.group : draggedTab);
|
||||
let tabSize = this._tabbrowserTabs.verticalMode ? tabHeight : tabWidth;
|
||||
let translateX = event.screenX - dragData.screenX;
|
||||
let translateY = event.screenY - dragData.screenY;
|
||||
@@ -1870,6 +1922,12 @@
|
||||
);
|
||||
let lastMovingTab = movingTabs.at(-1);
|
||||
let firstMovingTab = movingTabs[0];
|
||||
+ if (lastMovingTab.group?.hasAttribute("split-view-group")) {
|
||||
+ lastMovingTab = lastMovingTab.group;
|
||||
+ }
|
||||
+ if (firstMovingTab.group?.hasAttribute("split-view-group")) {
|
||||
+ firstMovingTab = firstMovingTab.group;
|
||||
+ }
|
||||
let endEdge = ele => ele[screenAxis] + bounds(ele)[size];
|
||||
let lastMovingTabScreen = endEdge(lastMovingTab);
|
||||
let firstMovingTabScreen = firstMovingTab[screenAxis];
|
||||
@@ -1884,6 +1942,13 @@
|
||||
let endBound = this._rtlMode
|
||||
? endEdge(this._tabbrowserTabs) - lastMovingTabScreen
|
||||
: periphery[screenAxis] - 1 - lastMovingTabScreen;
|
||||
+ {
|
||||
+ let firstTab = tabs.at(this._rtlMode ? -1 : 0);
|
||||
+ let lastTab = tabs.at(this._rtlMode ? 0 : -1);
|
||||
+ startBound = firstTab[screenAxis] - firstMovingTabScreen;
|
||||
+ endBound = endEdge(lastTab) - lastMovingTabScreen;
|
||||
+ endBound = gZenPinnedTabManager.getLastTabBound(endBound, lastTab, isDraggingFolder);
|
||||
+ }
|
||||
translate = Math.min(Math.max(translate, startBound), endBound);
|
||||
|
||||
// Center the tab under the cursor if the tab is not under the cursor while dragging
|
||||
@@ -2075,6 +2140,8 @@
|
||||
};
|
||||
|
||||
let dropElement = getOverlappedElement();
|
||||
+ if (dropElement?.hasAttribute("split-view-group")) dropElement = dropElement.labelElement;
|
||||
+ gZenPinnedTabManager.animateSeparatorMove(movingTabs, dropElement, isPinned, event);
|
||||
|
||||
let newDropElementIndex;
|
||||
if (dropElement) {
|
||||
@@ -2157,7 +2224,7 @@
|
||||
? Services.prefs.getIntPref(
|
||||
"browser.tabs.dragDrop.moveOverThresholdPercent"
|
||||
) / 100
|
||||
- : 0.5;
|
||||
+ : Services.prefs.getIntPref('zen.view.drag-and-drop.move-over-threshold') / 100;
|
||||
moveOverThreshold = Math.min(1, Math.max(0, moveOverThreshold));
|
||||
let shouldMoveOver = overlapPercent > moveOverThreshold;
|
||||
if (logicalForward && shouldMoveOver) {
|
||||
@@ -2190,6 +2257,7 @@
|
||||
// If dragging a group over another group, don't make it look like it is
|
||||
// possible to drop the dragged group inside the other group.
|
||||
if (
|
||||
+ false &&
|
||||
isTabGroupLabel(draggedTab) &&
|
||||
dropElement?.group &&
|
||||
(!dropElement.group.collapsed ||
|
||||
@@ -2216,20 +2284,13 @@
|
||||
let isOutOfBounds = isPinned
|
||||
? dropElement.elementIndex >= numPinned
|
||||
: dropElement.elementIndex < numPinned;
|
||||
- if (isOutOfBounds) {
|
||||
- // Drop after last pinned tab
|
||||
- dropElement = this._tabbrowserTabs.ariaFocusableItems[numPinned - 1];
|
||||
- dropBefore = false;
|
||||
- }
|
||||
}
|
||||
|
||||
- if (
|
||||
- gBrowser._tabGroupsEnabled &&
|
||||
- isTab(draggedTab) &&
|
||||
- !isPinned &&
|
||||
- (!numPinned || newDropElementIndex >= numPinned)
|
||||
- ) {
|
||||
+ if (isTab(draggedTab) || isTabGroupLabel(draggedTab)) {
|
||||
let dragOverGroupingThreshold = 1 - moveOverThreshold;
|
||||
+ if (draggedTab && !dropElement?.group) {
|
||||
+ gZenFolders.highlightGroupOnDragOver(null);
|
||||
+ }
|
||||
let groupingDelay = Services.prefs.getIntPref(
|
||||
"browser.tabs.dragDrop.createGroup.delayMS"
|
||||
);
|
||||
@@ -2237,6 +2298,7 @@
|
||||
// When dragging tab(s) over an ungrouped tab, signal to the user
|
||||
// that dropping the tab(s) will create a new tab group.
|
||||
let shouldCreateGroupOnDrop =
|
||||
+ false &&
|
||||
!movingTabsSet.has(dropElement) &&
|
||||
isTab(dropElement) &&
|
||||
!dropElement?.group &&
|
||||
@@ -2245,6 +2307,7 @@
|
||||
// When dragging tab(s) over a collapsed tab group label, signal to the
|
||||
// user that dropping the tab(s) will add them to the group.
|
||||
let shouldDropIntoCollapsedTabGroup =
|
||||
+ false &&
|
||||
isTabGroupLabel(dropElement) &&
|
||||
dropElement.group.collapsed &&
|
||||
overlapPercent > dragOverGroupingThreshold;
|
||||
@@ -2302,6 +2365,14 @@
|
||||
dropElement = dropElementGroup.tabs[0];
|
||||
dropBefore = true;
|
||||
}
|
||||
+ ({ dropElement, colorCode, dropBefore } = gZenFolders.handleDragOverTabGroupLabel(
|
||||
+ dropElement,
|
||||
+ draggedTab,
|
||||
+ overlapPercent,
|
||||
+ movingTabs,
|
||||
+ dropBefore,
|
||||
+ colorCode
|
||||
+ ));
|
||||
}
|
||||
this._setDragOverGroupColor(colorCode);
|
||||
this._tabbrowserTabs.toggleAttribute(
|
||||
@@ -2324,10 +2395,11 @@
|
||||
dragData.dropBefore = dropBefore;
|
||||
dragData.animDropElementIndex = newDropElementIndex;
|
||||
|
||||
+ gZenFolders.setFolderIndentation(movingTabs, dropElement);
|
||||
// Shift background tabs to leave a gap where the dragged tab
|
||||
// would currently be dropped.
|
||||
for (let item of tabs) {
|
||||
- if (item == draggedTab) {
|
||||
+ if (item == draggedTab || (item.group?.hasAttribute("split-view-group") && item.group == draggedTab.group)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2417,11 +2489,13 @@
|
||||
}
|
||||
|
||||
finishAnimateTabMove() {
|
||||
+ gZenPinnedTabManager.onDragFinish();
|
||||
if (!this.#isMovingTab()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#setMovingTabMode(false);
|
||||
+ gZenFolders.highlightGroupOnDragOver(null);
|
||||
|
||||
for (let item of this._tabbrowserTabs.ariaFocusableItems) {
|
||||
this._resetGroupTarget(item);
|
||||
@@ -2457,7 +2531,7 @@
|
||||
@@ -2457,7 +2489,7 @@
|
||||
tab.style.left = "";
|
||||
tab.style.top = "";
|
||||
tab.style.maxWidth = "";
|
||||
@@ -428,7 +230,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
||||
}
|
||||
for (let label of draggedTabDocument.getElementsByClassName(
|
||||
"tab-group-label-container"
|
||||
@@ -2467,7 +2541,7 @@
|
||||
@@ -2467,7 +2499,7 @@
|
||||
label.style.left = "";
|
||||
label.style.top = "";
|
||||
label.style.maxWidth = "";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
|
||||
index 2dacb325190b6ae42ebeb3e9f0e862dc690ecdca..23b134a8ba674978182415a8bac926f7600f564a 100644
|
||||
index 2dacb325190b6ae42ebeb3e9f0e862dc690ecdca..160e277d64eaac8408aed90eaf62606479424001 100644
|
||||
--- a/browser/components/tabbrowser/content/tab.js
|
||||
+++ b/browser/components/tabbrowser/content/tab.js
|
||||
@@ -21,6 +21,7 @@
|
||||
@@ -87,7 +87,7 @@ index 2dacb325190b6ae42ebeb3e9f0e862dc690ecdca..23b134a8ba674978182415a8bac926f7
|
||||
}
|
||||
|
||||
get lastAccessed() {
|
||||
@@ -382,7 +395,12 @@
|
||||
@@ -382,7 +395,18 @@
|
||||
}
|
||||
|
||||
get group() {
|
||||
@@ -97,11 +97,17 @@ index 2dacb325190b6ae42ebeb3e9f0e862dc690ecdca..23b134a8ba674978182415a8bac926f7
|
||||
+ }
|
||||
+ if (gBrowser.isTabGroup(this.parentElement?.parentElement)) {
|
||||
+ return this.parentElement.parentElement;
|
||||
+ }
|
||||
+ if (this.pinned) {
|
||||
+ let collapsiblePins = gZenWorkspaces.workspaceElement(this.getAttribute('zen-workspace-id'))?.collapsiblePins;
|
||||
+ if (collapsiblePins?.collapsed) {
|
||||
+ return collapsiblePins;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
get splitview() {
|
||||
@@ -473,6 +491,8 @@
|
||||
@@ -473,6 +497,8 @@
|
||||
this.style.MozUserFocus = "ignore";
|
||||
} else if (
|
||||
event.target.classList.contains("tab-close-button") ||
|
||||
@@ -110,7 +116,7 @@ index 2dacb325190b6ae42ebeb3e9f0e862dc690ecdca..23b134a8ba674978182415a8bac926f7
|
||||
event.target.classList.contains("tab-icon-overlay") ||
|
||||
event.target.classList.contains("tab-audio-button")
|
||||
) {
|
||||
@@ -527,6 +547,10 @@
|
||||
@@ -527,6 +553,10 @@
|
||||
this.style.MozUserFocus = "";
|
||||
}
|
||||
|
||||
@@ -121,7 +127,7 @@ index 2dacb325190b6ae42ebeb3e9f0e862dc690ecdca..23b134a8ba674978182415a8bac926f7
|
||||
on_click(event) {
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
@@ -587,6 +611,14 @@
|
||||
@@ -587,6 +617,14 @@
|
||||
// (see tabbrowser-tabs 'click' handler).
|
||||
gBrowser.tabContainer._blockDblClick = true;
|
||||
}
|
||||
@@ -136,7 +142,7 @@ index 2dacb325190b6ae42ebeb3e9f0e862dc690ecdca..23b134a8ba674978182415a8bac926f7
|
||||
}
|
||||
|
||||
on_dblclick(event) {
|
||||
@@ -610,6 +642,8 @@
|
||||
@@ -610,6 +648,8 @@
|
||||
animate: true,
|
||||
triggeringEvent: event,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d797de7be 100644
|
||||
index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..00a9810cc894b6a21adb78b70a15049cc1db3edf 100644
|
||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -386,6 +386,7 @@
|
||||
@@ -87,7 +87,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
tab.linkedPanel = uniqueId;
|
||||
this._selectedTab = tab;
|
||||
this._selectedBrowser = browser;
|
||||
@@ -898,13 +951,17 @@
|
||||
@@ -898,13 +951,18 @@
|
||||
}
|
||||
|
||||
this.showTab(aTab);
|
||||
@@ -100,17 +100,21 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
);
|
||||
// If periphery is null, append to end
|
||||
- this.pinnedTabsContainer.insertBefore(aTab, periphery);
|
||||
+ this.tabContainer.tabDragAndDrop.handle_drop_transition(this.tabs[this.pinnedTabCount - 1], aTab, [aTab], false);
|
||||
+ aTab.hasAttribute("zen-essential") ? gZenWorkspaces.getEssentialsSection(aTab).appendChild(aTab) : this.pinnedTabsContainer.insertBefore(aTab, this.pinnedTabsContainer.lastChild)
|
||||
});
|
||||
+ }
|
||||
|
||||
aTab.setAttribute("pinned", "true");
|
||||
this._updateTabBarForPinnedTabs();
|
||||
@@ -917,11 +974,15 @@
|
||||
@@ -917,11 +975,18 @@
|
||||
}
|
||||
|
||||
this.#handleTabMove(aTab, () => {
|
||||
+ const handled = gZenFolders.handleTabUnpin(aTab);
|
||||
+ if (!handled) {
|
||||
+ this.tabContainer.tabDragAndDrop.handle_drop_transition(this.tabs[this.pinnedTabCount + 1 /* empty + extra */], aTab, [aTab], true);
|
||||
+ }
|
||||
+
|
||||
// we remove this attribute first, so that allTabs represents
|
||||
// the moving of a tab from the pinned tabs container
|
||||
@@ -123,7 +127,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
});
|
||||
|
||||
aTab.style.marginInlineStart = "";
|
||||
@@ -1098,6 +1159,9 @@
|
||||
@@ -1098,6 +1163,9 @@
|
||||
|
||||
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
|
||||
|
||||
@@ -133,7 +137,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (
|
||||
aIconURL &&
|
||||
!LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))
|
||||
@@ -1107,6 +1171,9 @@
|
||||
@@ -1107,6 +1175,9 @@
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -143,7 +147,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
let browser = this.getBrowserForTab(aTab);
|
||||
browser.mIconURL = aIconURL;
|
||||
@@ -1379,7 +1446,6 @@
|
||||
@@ -1379,7 +1450,6 @@
|
||||
|
||||
// Preview mode should not reset the owner
|
||||
if (!this._previewMode && !oldTab.selected) {
|
||||
@@ -151,7 +155,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
|
||||
let lastRelatedTab = this._lastRelatedTabMap.get(oldTab);
|
||||
@@ -1470,6 +1536,7 @@
|
||||
@@ -1470,6 +1540,7 @@
|
||||
if (!this._previewMode) {
|
||||
newTab.recordTimeFromUnloadToReload();
|
||||
newTab.updateLastAccessed();
|
||||
@@ -159,7 +163,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
oldTab.updateLastAccessed();
|
||||
// if this is the foreground window, update the last-seen timestamps.
|
||||
if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
|
||||
@@ -1622,6 +1689,9 @@
|
||||
@@ -1622,6 +1693,9 @@
|
||||
}
|
||||
|
||||
let activeEl = document.activeElement;
|
||||
@@ -169,7 +173,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
// If focus is on the old tab, move it to the new tab.
|
||||
if (activeEl == oldTab) {
|
||||
newTab.focus();
|
||||
@@ -1945,6 +2015,11 @@
|
||||
@@ -1945,6 +2019,11 @@
|
||||
}
|
||||
|
||||
_setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL } = {}) {
|
||||
@@ -181,7 +185,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (!aLabel || aLabel.includes("about:reader?")) {
|
||||
return false;
|
||||
}
|
||||
@@ -2053,7 +2128,7 @@
|
||||
@@ -2053,7 +2132,7 @@
|
||||
newIndex = this.selectedTab._tPos + 1;
|
||||
}
|
||||
|
||||
@@ -190,7 +194,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (this.isTabGroupLabel(targetTab)) {
|
||||
throw new Error(
|
||||
"Replacing a tab group label with a tab is not supported"
|
||||
@@ -2328,6 +2403,7 @@
|
||||
@@ -2328,6 +2407,7 @@
|
||||
uriIsAboutBlank,
|
||||
userContextId,
|
||||
skipLoad,
|
||||
@@ -198,7 +202,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} = {}) {
|
||||
let b = document.createXULElement("browser");
|
||||
// Use the JSM global to create the permanentKey, so that if the
|
||||
@@ -2401,8 +2477,7 @@
|
||||
@@ -2401,8 +2481,7 @@
|
||||
// we use a different attribute name for this?
|
||||
b.setAttribute("name", name);
|
||||
}
|
||||
@@ -208,7 +212,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
b.setAttribute("transparent", "true");
|
||||
}
|
||||
|
||||
@@ -2567,7 +2642,7 @@
|
||||
@@ -2567,7 +2646,7 @@
|
||||
|
||||
let panel = this.getPanel(browser);
|
||||
let uniqueId = this._generateUniquePanelID();
|
||||
@@ -217,7 +221,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
aTab.linkedPanel = uniqueId;
|
||||
|
||||
// Inject the <browser> into the DOM if necessary.
|
||||
@@ -2626,8 +2701,8 @@
|
||||
@@ -2626,8 +2705,8 @@
|
||||
// If we transitioned from one browser to two browsers, we need to set
|
||||
// hasSiblings=false on both the existing browser and the new browser.
|
||||
if (this.tabs.length == 2) {
|
||||
@@ -228,7 +232,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} else {
|
||||
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
|
||||
}
|
||||
@@ -2814,7 +2889,6 @@
|
||||
@@ -2814,7 +2893,6 @@
|
||||
this.selectedTab = this.addTrustedTab(BROWSER_NEW_TAB_URL, {
|
||||
tabIndex: tab._tPos + 1,
|
||||
userContextId: tab.userContextId,
|
||||
@@ -236,7 +240,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
focusUrlBar: true,
|
||||
});
|
||||
resolve(this.selectedBrowser);
|
||||
@@ -2923,6 +2997,9 @@
|
||||
@@ -2923,6 +3001,9 @@
|
||||
schemelessInput,
|
||||
hasValidUserGestureActivation = false,
|
||||
textDirectiveUserActivation = false,
|
||||
@@ -246,7 +250,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} = {}
|
||||
) {
|
||||
// all callers of addTab that pass a params object need to pass
|
||||
@@ -2933,10 +3010,17 @@
|
||||
@@ -2933,10 +3014,17 @@
|
||||
);
|
||||
}
|
||||
|
||||
@@ -264,7 +268,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
// If we're opening a foreground tab, set the owner by default.
|
||||
ownerTab ??= inBackground ? null : this.selectedTab;
|
||||
|
||||
@@ -2944,6 +3028,7 @@
|
||||
@@ -2944,6 +3032,7 @@
|
||||
if (this.selectedTab.owner) {
|
||||
this.selectedTab.owner = null;
|
||||
}
|
||||
@@ -272,7 +276,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
// Find the tab that opened this one, if any. This is used for
|
||||
// determining positioning, and inherited attributes such as the
|
||||
@@ -2996,6 +3081,21 @@
|
||||
@@ -2996,6 +3085,21 @@
|
||||
noInitialLabel,
|
||||
skipBackgroundNotify,
|
||||
});
|
||||
@@ -294,7 +298,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (insertTab) {
|
||||
// Insert the tab into the tab container in the correct position.
|
||||
this.#insertTabAtIndex(t, {
|
||||
@@ -3004,6 +3104,7 @@
|
||||
@@ -3004,6 +3108,7 @@
|
||||
ownerTab,
|
||||
openerTab,
|
||||
pinned,
|
||||
@@ -302,7 +306,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
bulkOrderedOpen,
|
||||
tabGroup: tabGroup ?? openerTab?.group,
|
||||
});
|
||||
@@ -3022,6 +3123,7 @@
|
||||
@@ -3022,6 +3127,7 @@
|
||||
openWindowInfo,
|
||||
skipLoad,
|
||||
triggeringRemoteType,
|
||||
@@ -310,7 +314,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}));
|
||||
|
||||
if (focusUrlBar) {
|
||||
@@ -3146,6 +3248,12 @@
|
||||
@@ -3146,6 +3252,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +327,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
// Additionally send pinned tab events
|
||||
if (pinned) {
|
||||
this.#notifyPinnedStatus(t);
|
||||
@@ -3349,10 +3457,10 @@
|
||||
@@ -3349,10 +3461,10 @@
|
||||
isAdoptingGroup = false,
|
||||
isUserTriggered = false,
|
||||
telemetryUserCreateSource = "unknown",
|
||||
@@ -335,7 +339,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
|
||||
if (!color) {
|
||||
@@ -3373,9 +3481,14 @@
|
||||
@@ -3373,9 +3485,14 @@
|
||||
label,
|
||||
isAdoptingGroup
|
||||
);
|
||||
@@ -352,7 +356,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
);
|
||||
group.addTabs(tabs);
|
||||
|
||||
@@ -3496,7 +3609,7 @@
|
||||
@@ -3496,7 +3613,7 @@
|
||||
}
|
||||
|
||||
this.#handleTabMove(tab, () =>
|
||||
@@ -361,7 +365,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3698,6 +3811,7 @@
|
||||
@@ -3698,6 +3815,7 @@
|
||||
openWindowInfo,
|
||||
skipLoad,
|
||||
triggeringRemoteType,
|
||||
@@ -369,7 +373,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
) {
|
||||
// If we don't have a preferred remote type (or it is `NOT_REMOTE`), and
|
||||
@@ -3767,6 +3881,7 @@
|
||||
@@ -3767,6 +3885,7 @@
|
||||
openWindowInfo,
|
||||
name,
|
||||
skipLoad,
|
||||
@@ -377,7 +381,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3955,7 +4070,7 @@
|
||||
@@ -3955,7 +4074,7 @@
|
||||
// Add a new tab if needed.
|
||||
if (!tab) {
|
||||
let createLazyBrowser =
|
||||
@@ -386,7 +390,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
let url = "about:blank";
|
||||
if (tabData.entries?.length) {
|
||||
@@ -3992,8 +4107,10 @@
|
||||
@@ -3992,8 +4111,10 @@
|
||||
insertTab: false,
|
||||
skipLoad: true,
|
||||
preferredRemoteType,
|
||||
@@ -398,7 +402,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (select) {
|
||||
tabToSelect = tab;
|
||||
}
|
||||
@@ -4005,7 +4122,8 @@
|
||||
@@ -4005,7 +4126,8 @@
|
||||
this.pinTab(tab);
|
||||
// Then ensure all the tab open/pinning information is sent.
|
||||
this._fireTabOpen(tab, {});
|
||||
@@ -408,7 +412,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
let { groupId } = tabData;
|
||||
const tabGroup = tabGroupWorkingData.get(groupId);
|
||||
// if a tab refers to a tab group we don't know, skip any group
|
||||
@@ -4019,7 +4137,10 @@
|
||||
@@ -4019,7 +4141,10 @@
|
||||
tabGroup.stateData.id,
|
||||
tabGroup.stateData.color,
|
||||
tabGroup.stateData.collapsed,
|
||||
@@ -420,7 +424,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
);
|
||||
tabsFragment.appendChild(tabGroup.node);
|
||||
}
|
||||
@@ -4064,9 +4185,23 @@
|
||||
@@ -4064,9 +4189,23 @@
|
||||
// to remove the old selected tab.
|
||||
if (tabToSelect) {
|
||||
let leftoverTab = this.selectedTab;
|
||||
@@ -436,15 +440,15 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
+ gZenWorkspaces._initialTab._shouldRemove = true;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ }
|
||||
+ else {
|
||||
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
|
||||
+ }
|
||||
}
|
||||
+ this._hasAlreadyInitializedZenSessionStore = true;
|
||||
|
||||
if (tabs.length > 1 || !tabs[0].selected) {
|
||||
this._updateTabsAfterInsert();
|
||||
@@ -4257,11 +4392,14 @@
|
||||
@@ -4257,11 +4396,14 @@
|
||||
if (ownerTab) {
|
||||
tab.owner = ownerTab;
|
||||
}
|
||||
@@ -460,7 +464,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (
|
||||
!bulkOrderedOpen &&
|
||||
((openerTab &&
|
||||
@@ -4273,7 +4411,7 @@
|
||||
@@ -4273,7 +4415,7 @@
|
||||
let lastRelatedTab =
|
||||
openerTab && this._lastRelatedTabMap.get(openerTab);
|
||||
let previousTab = lastRelatedTab || openerTab || this.selectedTab;
|
||||
@@ -469,7 +473,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
tabGroup = previousTab.group;
|
||||
}
|
||||
if (
|
||||
@@ -4284,7 +4422,7 @@
|
||||
@@ -4284,7 +4426,7 @@
|
||||
) {
|
||||
elementIndex = Infinity;
|
||||
} else if (previousTab.visible) {
|
||||
@@ -478,7 +482,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} else if (previousTab == FirefoxViewHandler.tab) {
|
||||
elementIndex = 0;
|
||||
}
|
||||
@@ -4312,14 +4450,14 @@
|
||||
@@ -4312,14 +4454,14 @@
|
||||
}
|
||||
// Ensure index is within bounds.
|
||||
if (tab.pinned) {
|
||||
@@ -497,7 +501,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
if (pinned && !itemAfter?.pinned) {
|
||||
itemAfter = null;
|
||||
@@ -4330,7 +4468,7 @@
|
||||
@@ -4330,7 +4472,7 @@
|
||||
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
|
||||
@@ -506,7 +510,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (this.isTab(itemAfter) && itemAfter.group == tabGroup) {
|
||||
// Place at the front of, or between tabs in, the same tab group
|
||||
this.tabContainer.insertBefore(tab, itemAfter);
|
||||
@@ -4358,7 +4496,11 @@
|
||||
@@ -4358,7 +4500,11 @@
|
||||
const tabContainer = pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -518,7 +522,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
|
||||
this._updateTabsAfterInsert();
|
||||
@@ -4366,6 +4508,7 @@
|
||||
@@ -4366,6 +4512,7 @@
|
||||
if (pinned) {
|
||||
this._updateTabBarForPinnedTabs();
|
||||
}
|
||||
@@ -526,7 +530,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -4916,6 +5059,7 @@
|
||||
@@ -4916,6 +5063,7 @@
|
||||
telemetrySource,
|
||||
} = {}
|
||||
) {
|
||||
@@ -534,7 +538,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
||||
// can be considered equivalent to closing the window.
|
||||
if (
|
||||
@@ -5005,6 +5149,7 @@
|
||||
@@ -5005,6 +5153,7 @@
|
||||
if (lastToClose) {
|
||||
this.removeTab(lastToClose, aParams);
|
||||
}
|
||||
@@ -542,7 +546,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -5043,6 +5188,12 @@
|
||||
@@ -5043,6 +5192,12 @@
|
||||
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
|
||||
}
|
||||
|
||||
@@ -555,7 +559,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
if (!animate && aTab.closing) {
|
||||
@@ -5057,6 +5208,9 @@
|
||||
@@ -5057,6 +5212,9 @@
|
||||
// state).
|
||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||
let isLastTab = this.#isLastTabInWindow(aTab);
|
||||
@@ -565,7 +569,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (
|
||||
!this._beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -5105,7 +5259,13 @@
|
||||
@@ -5105,7 +5263,13 @@
|
||||
// We're not animating, so we can cancel the animation stopwatch.
|
||||
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
||||
aTab._closeTimeAnimTimerId = null;
|
||||
@@ -580,7 +584,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5239,7 +5399,7 @@
|
||||
@@ -5239,7 +5403,7 @@
|
||||
closeWindowWithLastTab != null
|
||||
? closeWindowWithLastTab
|
||||
: !window.toolbar.visible ||
|
||||
@@ -589,7 +593,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
if (closeWindow) {
|
||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||
@@ -5263,6 +5423,7 @@
|
||||
@@ -5263,6 +5427,7 @@
|
||||
|
||||
newTab = true;
|
||||
}
|
||||
@@ -597,7 +601,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||
|
||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||
@@ -5303,13 +5464,7 @@
|
||||
@@ -5303,13 +5468,7 @@
|
||||
aTab._mouseleave();
|
||||
|
||||
if (newTab) {
|
||||
@@ -612,7 +616,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} else {
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -5442,6 +5597,7 @@
|
||||
@@ -5442,6 +5601,7 @@
|
||||
this.tabs[i]._tPos = i;
|
||||
}
|
||||
|
||||
@@ -620,7 +624,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (!this._windowIsClosing) {
|
||||
// update tab close buttons state
|
||||
this.tabContainer._updateCloseButtons();
|
||||
@@ -5663,6 +5819,7 @@
|
||||
@@ -5663,6 +5823,7 @@
|
||||
}
|
||||
|
||||
let excludeTabs = new Set(aExcludeTabs);
|
||||
@@ -628,7 +632,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
// If this tab has a successor, it should be selectable, since
|
||||
// hiding or closing a tab removes that tab as a successor.
|
||||
@@ -5675,13 +5832,13 @@
|
||||
@@ -5675,13 +5836,13 @@
|
||||
!excludeTabs.has(aTab.owner) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||
) {
|
||||
@@ -644,7 +648,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
);
|
||||
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
@@ -5697,7 +5854,7 @@
|
||||
@@ -5697,7 +5858,7 @@
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
@@ -653,7 +657,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
|
||||
// If no qualifying visible tab was found, see if there is a tab in
|
||||
@@ -5718,7 +5875,7 @@
|
||||
@@ -5718,7 +5879,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
@@ -662,7 +666,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
|
||||
_blurTab(aTab) {
|
||||
@@ -5729,7 +5886,7 @@
|
||||
@@ -5729,7 +5890,7 @@
|
||||
* @returns {boolean}
|
||||
* False if swapping isn't permitted, true otherwise.
|
||||
*/
|
||||
@@ -671,7 +675,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
// Do not allow transfering a private tab to a non-private window
|
||||
// and vice versa.
|
||||
if (
|
||||
@@ -5783,6 +5940,7 @@
|
||||
@@ -5783,6 +5944,7 @@
|
||||
// fire the beforeunload event in the process. Close the other
|
||||
// window if this was its last tab.
|
||||
if (
|
||||
@@ -679,7 +683,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
!remoteBrowser._beginRemoveTab(aOtherTab, {
|
||||
adoptedByTab: aOurTab,
|
||||
closeWindowWithLastTab: true,
|
||||
@@ -5794,7 +5952,7 @@
|
||||
@@ -5794,7 +5956,7 @@
|
||||
// If this is the last tab of the window, hide the window
|
||||
// immediately without animation before the docshell swap, to avoid
|
||||
// about:blank being painted.
|
||||
@@ -688,7 +692,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (closeWindow) {
|
||||
let win = aOtherTab.ownerGlobal;
|
||||
win.windowUtils.suppressAnimation(true);
|
||||
@@ -5918,11 +6076,13 @@
|
||||
@@ -5918,11 +6080,13 @@
|
||||
}
|
||||
|
||||
// Finish tearing down the tab that's going away.
|
||||
@@ -702,7 +706,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
|
||||
this.setTabTitle(aOurTab);
|
||||
|
||||
@@ -6124,10 +6284,10 @@
|
||||
@@ -6124,10 +6288,10 @@
|
||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||
}
|
||||
|
||||
@@ -715,7 +719,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||
@@ -6185,7 +6345,8 @@
|
||||
@@ -6185,7 +6349,8 @@
|
||||
*
|
||||
* @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab
|
||||
*/
|
||||
@@ -725,7 +729,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (this.tabs.length == 1) {
|
||||
return null;
|
||||
}
|
||||
@@ -6209,12 +6370,14 @@
|
||||
@@ -6209,12 +6374,14 @@
|
||||
}
|
||||
|
||||
// tell a new window to take the "dropped" tab
|
||||
@@ -741,7 +745,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6319,7 +6482,7 @@
|
||||
@@ -6319,7 +6486,7 @@
|
||||
* `true` if element is a `<tab-group>`
|
||||
*/
|
||||
isTabGroup(element) {
|
||||
@@ -750,7 +754,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6404,8 +6567,8 @@
|
||||
@@ -6404,8 +6571,8 @@
|
||||
}
|
||||
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
@@ -761,7 +765,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} else {
|
||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||
}
|
||||
@@ -6431,10 +6594,16 @@
|
||||
@@ -6431,10 +6598,16 @@
|
||||
this.#handleTabMove(
|
||||
element,
|
||||
() => {
|
||||
@@ -780,7 +784,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
||||
neighbor.after(element);
|
||||
} else {
|
||||
@@ -6492,23 +6661,28 @@
|
||||
@@ -6492,23 +6665,28 @@
|
||||
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
||||
if (this.isTabGroupLabel(targetElement)) {
|
||||
targetElement = targetElement.group;
|
||||
@@ -815,7 +819,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||
// If the caller asks to move an unpinned element next to a pinned
|
||||
// tab, move the unpinned element to be the first unpinned element
|
||||
@@ -6521,14 +6695,34 @@
|
||||
@@ -6521,14 +6699,34 @@
|
||||
// move the tab group right before the first unpinned tab.
|
||||
// 4. Moving a tab group and the first unpinned tab is grouped:
|
||||
// move the tab group right before the first unpinned tab's tab group.
|
||||
@@ -851,7 +855,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
element.pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -6537,7 +6731,7 @@
|
||||
@@ -6537,7 +6735,7 @@
|
||||
element,
|
||||
() => {
|
||||
if (moveBefore) {
|
||||
@@ -860,7 +864,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
} else if (targetElement) {
|
||||
targetElement.after(element);
|
||||
} else {
|
||||
@@ -6607,10 +6801,10 @@
|
||||
@@ -6607,10 +6805,10 @@
|
||||
* @param {TabMetricsContext} [metricsContext]
|
||||
*/
|
||||
moveTabToGroup(aTab, aGroup, metricsContext) {
|
||||
@@ -873,7 +877,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
return;
|
||||
}
|
||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||
@@ -6656,6 +6850,7 @@
|
||||
@@ -6656,6 +6854,7 @@
|
||||
|
||||
let state = {
|
||||
tabIndex: tab._tPos,
|
||||
@@ -881,7 +885,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
};
|
||||
if (tab.visible) {
|
||||
state.elementIndex = tab.elementIndex;
|
||||
@@ -6682,7 +6877,7 @@
|
||||
@@ -6682,7 +6881,7 @@
|
||||
let changedTabGroup =
|
||||
previousTabState.tabGroupId != currentTabState.tabGroupId;
|
||||
|
||||
@@ -890,7 +894,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
tab.dispatchEvent(
|
||||
new CustomEvent("TabMove", {
|
||||
bubbles: true,
|
||||
@@ -6723,6 +6918,10 @@
|
||||
@@ -6723,6 +6922,10 @@
|
||||
|
||||
moveActionCallback();
|
||||
|
||||
@@ -901,17 +905,16 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||
// changed.
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
@@ -6815,6 +7014,9 @@
|
||||
@@ -6815,6 +7018,8 @@
|
||||
params.userContextId = aTab.getAttribute("usercontextid");
|
||||
}
|
||||
let newTab = this.addWebTab("about:blank", params);
|
||||
+ newTab._zenContentsVisible = true;
|
||||
+ newTab.zenStaticLabel = aTab.zenStaticLabel;
|
||||
+ newTab.zenStaticIcon = aTab.zenStaticIcon;
|
||||
let newBrowser = this.getBrowserForTab(newTab);
|
||||
|
||||
aTab.container.tabDragAndDrop.finishAnimateTabMove();
|
||||
@@ -7623,7 +7825,7 @@
|
||||
@@ -7623,7 +7828,7 @@
|
||||
// preventDefault(). It will still raise the window if appropriate.
|
||||
break;
|
||||
}
|
||||
@@ -920,7 +923,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
window.focus();
|
||||
aEvent.preventDefault();
|
||||
break;
|
||||
@@ -7640,7 +7842,6 @@
|
||||
@@ -7640,7 +7845,6 @@
|
||||
}
|
||||
case "TabGroupCollapse":
|
||||
aEvent.target.tabs.forEach(tab => {
|
||||
@@ -928,7 +931,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
});
|
||||
break;
|
||||
case "TabGroupCreateByUser":
|
||||
@@ -8589,6 +8790,7 @@
|
||||
@@ -8589,6 +8793,7 @@
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this.mTab.setAttribute("busy", "true");
|
||||
@@ -936,7 +939,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||
}
|
||||
@@ -8670,6 +8872,7 @@
|
||||
@@ -8670,6 +8875,7 @@
|
||||
// known defaults. Note we use the original URL since about:newtab
|
||||
// redirects to a prerendered page.
|
||||
const shouldRemoveFavicon =
|
||||
@@ -944,7 +947,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d
|
||||
!this.mBrowser.mIconURL &&
|
||||
!ignoreBlank &&
|
||||
!(originalLocation.spec in FAVICON_DEFAULTS);
|
||||
@@ -9623,7 +9826,7 @@ var TabContextMenu = {
|
||||
@@ -9623,7 +9829,7 @@ var TabContextMenu = {
|
||||
);
|
||||
contextUnpinSelectedTabs.hidden =
|
||||
!this.contextTab.pinned || !this.multiselected;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js
|
||||
index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057ebc71c2f5f 100644
|
||||
index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..e92927612abf12631c384a8f54b6a607fb699424 100644
|
||||
--- a/browser/components/tabbrowser/content/tabgroup.js
|
||||
+++ b/browser/components/tabbrowser/content/tabgroup.js
|
||||
@@ -14,11 +14,11 @@
|
||||
@@ -68,10 +68,10 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
- return false;
|
||||
- });
|
||||
+ this.appendChild = function (child) {
|
||||
+ this.querySelector(".tab-group-container").appendChild(child);
|
||||
+ this.groupContainer.appendChild(child);
|
||||
+ for (let tab of this.tabs) {
|
||||
+ if (tab.hasAttribute("zen-empty-tab") && tab.group === this) {
|
||||
+ this.querySelector(".zen-tab-group-start").after(tab);
|
||||
+ this.groupStartElement.after(tab);
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
@@ -92,7 +92,7 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
});
|
||||
}
|
||||
- this.#tabChangeObserver.observe(this, { childList: true });
|
||||
+ const container = this.querySelector(".tab-group-container");
|
||||
+ const container = this.groupContainer;
|
||||
+ if (container) {
|
||||
+ this.#tabChangeObserver.observe(container, { childList: true });
|
||||
+ }
|
||||
@@ -117,7 +117,7 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
});
|
||||
}
|
||||
|
||||
@@ -466,13 +492,57 @@
|
||||
@@ -466,13 +492,65 @@
|
||||
* @returns {MozTabbrowserTab[]}
|
||||
*/
|
||||
get tabs() {
|
||||
@@ -126,20 +126,29 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
- if (childrenArray[i].tagName == "tab-split-view-wrapper") {
|
||||
- childrenArray.splice(i, 1, ...childrenArray[i].tabs);
|
||||
+ // add other group tabs if they are under this group
|
||||
+ let childs = Array.from(this.querySelector(".tab-group-container")?.children ?? []);
|
||||
+ let childs = Array.from(this.groupContainer?.children ?? []);
|
||||
+ const tabsCollect = [];
|
||||
+ for (let item of childs) {
|
||||
+ tabsCollect.push(item);
|
||||
+ if (gBrowser.isTabGroup(item)) {
|
||||
+ tabsCollect.push(...item.tabs);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
- return childrenArray.filter(node => node.matches("tab"));
|
||||
+ return tabsCollect.filter(node => node.matches("tab"));
|
||||
+ }
|
||||
+
|
||||
+ get groupContainer() {
|
||||
+ return this.querySelector(".tab-group-container");
|
||||
+ }
|
||||
+
|
||||
+ get groupStartElement() {
|
||||
+ return this.querySelector(".zen-tab-group-start");
|
||||
+ }
|
||||
+
|
||||
+ get childGroupsAndTabs() {
|
||||
+ const result = [];
|
||||
+ const container = this.querySelector(".tab-group-container");
|
||||
+ const container = this.groupContainer;
|
||||
+
|
||||
+ for (const item of Array.from(container.children)) {
|
||||
+ if (gBrowser.isTab(item)) {
|
||||
@@ -169,9 +178,8 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
+ currentGroup = currentGroup?.group;
|
||||
+ if (currentGroup.collapsed) {
|
||||
+ return false;
|
||||
}
|
||||
}
|
||||
- return childrenArray.filter(node => node.matches("tab"));
|
||||
+ }
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
@@ -180,7 +188,7 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,7 +623,6 @@
|
||||
@@ -553,7 +631,6 @@
|
||||
addTabs(tabs, metricsContext) {
|
||||
for (let tab of tabs) {
|
||||
if (tab.pinned) {
|
||||
@@ -188,7 +196,7 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
}
|
||||
let tabToMove =
|
||||
this.ownerGlobal === tab.ownerGlobal
|
||||
@@ -616,7 +685,7 @@
|
||||
@@ -616,7 +693,7 @@
|
||||
*/
|
||||
on_click(event) {
|
||||
let isToggleElement =
|
||||
@@ -197,7 +205,7 @@ index 394b2af2e187b82bb3e98ebcdc6e66b63036e20d..4757555c7654a14578a5d057323057eb
|
||||
event.target === this.#overflowCountLabel;
|
||||
if (isToggleElement && event.button === 0) {
|
||||
event.preventDefault();
|
||||
@@ -687,5 +756,6 @@
|
||||
@@ -687,5 +764,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
||||
index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac8793422474052f476573 100644
|
||||
index 6b6c04599fe80983d13d2069ca62b99d8ad70271..6d5ae983446bc778f3075d79f8ff14748dd7756f 100644
|
||||
--- a/browser/components/tabbrowser/content/tabs.js
|
||||
+++ b/browser/components/tabbrowser/content/tabs.js
|
||||
@@ -235,7 +235,7 @@
|
||||
true
|
||||
)
|
||||
? new window.TabStacking(this)
|
||||
- : new window.TabDragAndDrop(this);
|
||||
+ : Services.prefs.getBoolPref("zen.tabs.use-legacy-drag-and-drop") ? new window.TabDragAndDrop(this) : new window.ZenDragAndDrop(this);
|
||||
this.tabDragAndDrop.init();
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@
|
||||
// and we're not hitting the scroll buttons.
|
||||
if (
|
||||
@@ -54,7 +63,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
return this.hasAttribute("overflow");
|
||||
}
|
||||
|
||||
@@ -837,29 +839,54 @@
|
||||
@@ -837,29 +839,56 @@
|
||||
if (pinnedChildren?.at(-1)?.id == "pinned-tabs-container-periphery") {
|
||||
pinnedChildren.pop();
|
||||
}
|
||||
@@ -81,6 +90,8 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
+ tabs.splice(i, 1);
|
||||
+ // add the tabs in the group to the list
|
||||
+ tabs.splice(i, 0, ...tab.tabs);
|
||||
+ } else if (tab.classList.contains("zen-tab-group-start")) {
|
||||
+ tabs.splice(i, 1);
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
@@ -119,7 +130,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -926,17 +953,10 @@
|
||||
@@ -926,17 +955,10 @@
|
||||
|
||||
let elementIndex = 0;
|
||||
|
||||
@@ -139,7 +150,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
if (isTab(child) && child.visible) {
|
||||
child.elementIndex = elementIndex++;
|
||||
focusableItems.push(child);
|
||||
@@ -944,11 +964,13 @@
|
||||
@@ -944,11 +966,13 @@
|
||||
child.labelElement.elementIndex = elementIndex++;
|
||||
focusableItems.push(child.labelElement);
|
||||
|
||||
@@ -154,7 +165,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
} else if (child.tagName == "tab-split-view-wrapper") {
|
||||
let visibleTabsInSplitView = child.tabs.filter(tab => tab.visible);
|
||||
visibleTabsInSplitView.forEach(tab => {
|
||||
@@ -992,6 +1014,7 @@
|
||||
@@ -992,6 +1016,7 @@
|
||||
_invalidateCachedTabs() {
|
||||
this.#allTabs = null;
|
||||
this._invalidateCachedVisibleTabs();
|
||||
@@ -162,7 +173,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
}
|
||||
|
||||
_invalidateCachedVisibleTabs() {
|
||||
@@ -1095,7 +1118,7 @@
|
||||
@@ -1095,7 +1120,7 @@
|
||||
|
||||
if (node == null) {
|
||||
// We have a container for non-tab elements at the end of the scrollbox.
|
||||
@@ -171,7 +182,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
}
|
||||
|
||||
node.before(tab);
|
||||
@@ -1193,7 +1216,7 @@
|
||||
@@ -1193,7 +1218,7 @@
|
||||
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
|
||||
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
|
||||
// Attach the long click popup to all of them.
|
||||
@@ -180,7 +191,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
const newTab2 = this.newTabButton;
|
||||
const newTabVertical = document.getElementById(
|
||||
"vertical-tabs-newtab-button"
|
||||
@@ -1294,8 +1317,10 @@
|
||||
@@ -1294,8 +1319,10 @@
|
||||
*/
|
||||
_handleTabSelect(aInstant) {
|
||||
let selectedTab = this.selectedItem;
|
||||
@@ -191,7 +202,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
selectedTab._notselectedsinceload = false;
|
||||
}
|
||||
|
||||
@@ -1304,7 +1329,7 @@
|
||||
@@ -1304,7 +1331,7 @@
|
||||
* @param {boolean} [shouldScrollInstantly=false]
|
||||
*/
|
||||
#ensureTabIsVisible(tab, shouldScrollInstantly = false) {
|
||||
@@ -200,7 +211,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac879342247405
|
||||
if (arrowScrollbox?.overflowing) {
|
||||
arrowScrollbox.ensureElementIsVisible(tab, shouldScrollInstantly);
|
||||
}
|
||||
@@ -1437,7 +1462,7 @@
|
||||
@@ -1437,7 +1464,7 @@
|
||||
}
|
||||
|
||||
_notifyBackgroundTab(aTab) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
.subviewbutton,
|
||||
#zen-welcome-start-button,
|
||||
.zen-toast button,
|
||||
.zen-current-workspace-indicator-chevron,
|
||||
.pinned-tabs-container-separator toolbarbutton {
|
||||
-moz-context-properties: fill, fill-opacity !important;
|
||||
fill: currentColor !important;
|
||||
@@ -116,7 +117,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
#zen-rice-share-options .options-header,
|
||||
.zen-current-workspace-indicator-chevron,
|
||||
#PanelUI-zen-gradient-generator-color-page-right {
|
||||
list-style-image: url('arrow-right.svg');
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
# 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/.
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 18 18"><g stroke-linecap="round" stroke-width="1.5" fill="none" stroke="context-fill" stroke-opacity="context-fill-opacity" stroke-linejoin="round" class="nc-icon-wrapper"><polyline points="6.5 2.75 12.75 9 6.5 15.25"></polyline></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 18 18"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="context-fill" stroke-opacity="context-fill-opacity" stroke-linejoin="round" class="nc-icon-wrapper"><polyline points="6.5 2.75 12.75 9 6.5 15.25"></polyline></g></svg>
|
||||
|
||||
47
src/widget/cocoa/nsDragService-mm.patch
Normal file
47
src/widget/cocoa/nsDragService-mm.patch
Normal file
@@ -0,0 +1,47 @@
|
||||
diff --git a/widget/cocoa/nsDragService.mm b/widget/cocoa/nsDragService.mm
|
||||
index f1614b823a859ff8fbc74982f205bb1f2ef29beb..897c24846a97c132babe3ad79da12ebfcec90484 100644
|
||||
--- a/widget/cocoa/nsDragService.mm
|
||||
+++ b/widget/cocoa/nsDragService.mm
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
+#include "mozilla/nsZenDragAndDrop.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsCocoaUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
@@ -148,6 +149,10 @@
|
||||
bitsPerPixel:32];
|
||||
|
||||
uint8_t* dest = [imageRep bitmapData];
|
||||
+ auto drag_translucency = DRAG_TRANSLUCENCY;
|
||||
+ if (auto zenDragAndDrop = zen::nsZenDragAndDrop::GetZenDragAndDropInstance()) {
|
||||
+ drag_translucency = zenDragAndDrop->GetDragImageOpacity();
|
||||
+ }
|
||||
for (uint32_t i = 0; i < height; ++i) {
|
||||
uint8_t* src = map.mData + i * map.mStride;
|
||||
for (uint32_t j = 0; j < width; ++j) {
|
||||
@@ -155,15 +160,15 @@
|
||||
// is premultipled here. Also, Quartz likes RGBA, so do that translation
|
||||
// as well.
|
||||
#ifdef IS_BIG_ENDIAN
|
||||
- dest[0] = uint8_t(src[1] * DRAG_TRANSLUCENCY);
|
||||
- dest[1] = uint8_t(src[2] * DRAG_TRANSLUCENCY);
|
||||
- dest[2] = uint8_t(src[3] * DRAG_TRANSLUCENCY);
|
||||
- dest[3] = uint8_t(src[0] * DRAG_TRANSLUCENCY);
|
||||
+ dest[0] = uint8_t(src[1] * drag_translucency);
|
||||
+ dest[1] = uint8_t(src[2] * drag_translucency);
|
||||
+ dest[2] = uint8_t(src[3] * drag_translucency);
|
||||
+ dest[3] = uint8_t(src[0] * drag_translucency);
|
||||
#else
|
||||
- dest[0] = uint8_t(src[2] * DRAG_TRANSLUCENCY);
|
||||
- dest[1] = uint8_t(src[1] * DRAG_TRANSLUCENCY);
|
||||
- dest[2] = uint8_t(src[0] * DRAG_TRANSLUCENCY);
|
||||
- dest[3] = uint8_t(src[3] * DRAG_TRANSLUCENCY);
|
||||
+ dest[0] = uint8_t(src[2] * drag_translucency);
|
||||
+ dest[1] = uint8_t(src[1] * drag_translucency);
|
||||
+ dest[2] = uint8_t(src[0] * drag_translucency);
|
||||
+ dest[3] = uint8_t(src[3] * drag_translucency);
|
||||
#endif
|
||||
src += 4;
|
||||
dest += 4;
|
||||
@@ -11,4 +11,6 @@
|
||||
ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenMods.mjs", { global: "current" });
|
||||
ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenKeyboardShortcuts.mjs", { global: "current" });
|
||||
ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenSessionStore.mjs", { global: "current" });
|
||||
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenDragAndDrop.js", this);
|
||||
}
|
||||
|
||||
@@ -125,8 +125,6 @@ window.gZenUIManager = {
|
||||
}
|
||||
menu.setAttribute('hidden', 'true');
|
||||
}
|
||||
// The first separator in the tab context menu is now useless.
|
||||
document.getElementById('tabContextMenu').querySelector('menuseparator').remove();
|
||||
},
|
||||
|
||||
_initCreateNewPopup() {
|
||||
@@ -1225,6 +1223,7 @@ window.gZenVerticalTabsManager = {
|
||||
|
||||
// Always move the splitter next to the sidebar
|
||||
const splitter = document.getElementById('zen-sidebar-splitter');
|
||||
splitter.addEventListener('dragover', gBrowser.tabContainer);
|
||||
this.navigatorToolbox.after(splitter);
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
if (!isCompactMode) {
|
||||
|
||||
1223
src/zen/drag-and-drop/ZenDragAndDrop.js
Normal file
1223
src/zen/drag-and-drop/ZenDragAndDrop.js
Normal file
File diff suppressed because it is too large
Load Diff
14
src/zen/drag-and-drop/components.conf
Normal file
14
src/zen/drag-and-drop/components.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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/.
|
||||
|
||||
Classes = [
|
||||
{
|
||||
'cid': '{f8714110-1fb1-4129-abad-887a64e4085e}',
|
||||
'interfaces': ['nsIZenDragAndDrop'],
|
||||
'contract_ids': ['@mozilla.org/zen/drag-and-drop;1'],
|
||||
'type': 'zen::nsZenDragAndDrop',
|
||||
'headers': ['mozilla/nsZenDragAndDrop.h'],
|
||||
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
|
||||
},
|
||||
]
|
||||
5
src/zen/drag-and-drop/jar.inc.mn
Normal file
5
src/zen/drag-and-drop/jar.inc.mn
Normal file
@@ -0,0 +1,5 @@
|
||||
# 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/ZenDragAndDrop.js (../../zen/drag-and-drop/ZenDragAndDrop.js)
|
||||
31
src/zen/drag-and-drop/moz.build
Normal file
31
src/zen/drag-and-drop/moz.build
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# 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/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
"nsIZenDragAndDrop.idl",
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
"nsZenDragAndDrop.h",
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
"nsZenDragAndDrop.cpp",
|
||||
]
|
||||
|
||||
XPCOM_MANIFESTS += [
|
||||
"components.conf",
|
||||
]
|
||||
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
"/dom/base",
|
||||
"/layout/base",
|
||||
"/widget",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
XPIDL_MODULE = "zen_dnd"
|
||||
25
src/zen/drag-and-drop/nsIZenDragAndDrop.idl
Normal file
25
src/zen/drag-and-drop/nsIZenDragAndDrop.idl
Normal file
@@ -0,0 +1,25 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* @brief Interface for Zen's drag and drop functionality.
|
||||
*/
|
||||
[scriptable, uuid(f8714110-1fb1-4129-abad-887a64e4085e)]
|
||||
interface nsIZenDragAndDrop : nsISupports {
|
||||
/**
|
||||
* @brief Indicate that a drag operation has started. Note
|
||||
* that this should only be called for zen's drag and drop
|
||||
* operations for the tabs.
|
||||
* @param opacity The opacity of the drag image.
|
||||
*/
|
||||
void onDragStart(in float opacity);
|
||||
|
||||
/**
|
||||
* @brief Indicate that a drag operation has ended.
|
||||
*/
|
||||
void onDragEnd();
|
||||
};
|
||||
|
||||
48
src/zen/drag-and-drop/nsZenDragAndDrop.cpp
Normal file
48
src/zen/drag-and-drop/nsZenDragAndDrop.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "nsZenDragAndDrop.h"
|
||||
#include "nsBaseDragService.h"
|
||||
|
||||
namespace zen {
|
||||
namespace {
|
||||
|
||||
static constexpr auto kZenDefaultDragImageOpacity =
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
// For GTK, the default is 0.5 (DRAG_IMAGE_ALPHA_LEVEL) to match
|
||||
// the native behavior. Make sure its synced with the following variable:
|
||||
// https://searchfox.org/firefox-main/rev/14c08f0368ead8bfdddec62f43e0bb5c8fd61289/widget/gtk/nsDragService.cpp#75
|
||||
0.5f;
|
||||
#else
|
||||
// For other platforms, the default is whatever the value of DRAG_TRANSLUCENCY
|
||||
// is, defined in nsBaseDragService.h
|
||||
DRAG_TRANSLUCENCY;
|
||||
#endif
|
||||
|
||||
} // namespace: <empty>
|
||||
|
||||
// Use the macro to inject all of the definitions for nsISupports.
|
||||
NS_IMPL_ISUPPORTS(nsZenDragAndDrop, nsIZenDragAndDrop)
|
||||
|
||||
nsZenDragAndDrop::nsZenDragAndDrop() {
|
||||
(void)this->OnDragEnd();
|
||||
}
|
||||
|
||||
auto nsZenDragAndDrop::GetZenDragAndDropInstance() -> nsCOMPtr<nsZenDragAndDrop> {
|
||||
return do_GetService(ZEN_BOOSTS_BACKEND_CONTRACTID);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsZenDragAndDrop::OnDragStart(float opacity) {
|
||||
mDragImageOpacity = opacity;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsZenDragAndDrop::OnDragEnd() {
|
||||
mDragImageOpacity = kZenDefaultDragImageOpacity;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace: zen
|
||||
45
src/zen/drag-and-drop/nsZenDragAndDrop.h
Normal file
45
src/zen/drag-and-drop/nsZenDragAndDrop.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_ZenDragAndDrop_h__
|
||||
#define mozilla_ZenDragAndDrop_h__
|
||||
|
||||
#include "nsIZenDragAndDrop.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#define ZEN_BOOSTS_BACKEND_CONTRACTID "@mozilla.org/zen/drag-and-drop;1"
|
||||
|
||||
namespace zen {
|
||||
|
||||
/**
|
||||
* @brief Implementation of the nsIZenDragAndDrop interface.
|
||||
* When we want to do a drag and drop operation, web standards
|
||||
* don't really allow much customization of the drag image.
|
||||
* This class allows Zen to have more control over the drag
|
||||
* and drop operations for the tabs.
|
||||
*/
|
||||
class nsZenDragAndDrop final : public nsIZenDragAndDrop {
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIZENDRAGANDDROP
|
||||
|
||||
public:
|
||||
explicit nsZenDragAndDrop();
|
||||
auto GetDragImageOpacity() const { return mDragImageOpacity; }
|
||||
|
||||
/**
|
||||
* @brief Get the singleton instance of nsZenDragAndDrop. There may be occasions
|
||||
* where it won't be available (e.g. on the content process), so this may return
|
||||
* nullptr.
|
||||
* @return nsZenDragAndDrop* The singleton instance, or nullptr if not available
|
||||
*/
|
||||
static auto GetZenDragAndDropInstance() -> nsCOMPtr<nsZenDragAndDrop>;
|
||||
|
||||
private:
|
||||
~nsZenDragAndDrop() = default;
|
||||
float mDragImageOpacity{};
|
||||
};
|
||||
|
||||
} // namespace zen
|
||||
|
||||
#endif
|
||||
@@ -2,11 +2,11 @@
|
||||
// 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/.
|
||||
|
||||
class ZenFolder extends MozTabbrowserTabGroup {
|
||||
export class nsZenFolder extends MozTabbrowserTabGroup {
|
||||
#initialized = false;
|
||||
|
||||
static markup = `
|
||||
<hbox class="tab-group-label-container" pack="center">
|
||||
<hbox class="tab-group-label-container zen-drop-target" pack="center">
|
||||
<html:div class="tab-group-folder-icon"/>
|
||||
<label class="tab-group-label" role="button"/>
|
||||
<image class="tab-reset-button reset-icon" role="button" keyNav="false" data-l10n-id="zen-folders-unload-all-tooltip"/>
|
||||
@@ -68,7 +68,7 @@ class ZenFolder extends MozTabbrowserTabGroup {
|
||||
}
|
||||
this.#initialized = true;
|
||||
this._activeTabs = [];
|
||||
this.icon.appendChild(ZenFolder.rawIcon.cloneNode(true));
|
||||
this.icon.appendChild(nsZenFolder.rawIcon.cloneNode(true));
|
||||
|
||||
this.labelElement.parentElement.setAttribute('context', 'zenFolderActions');
|
||||
|
||||
@@ -81,7 +81,7 @@ class ZenFolder extends MozTabbrowserTabGroup {
|
||||
};
|
||||
|
||||
if (this.collapsed) {
|
||||
this.querySelector('.tab-group-container').setAttribute('hidden', true);
|
||||
this.groupContainer.setAttribute('hidden', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ class ZenFolder extends MozTabbrowserTabGroup {
|
||||
gZenFolders.createFolder([], {
|
||||
renameFolder: !gZenUIManager.testingEnabled,
|
||||
label: 'Subfolder',
|
||||
insertAfter: this.querySelector('.tab-group-container').lastElementChild,
|
||||
insertAfter: this.groupContainer.lastElementChild,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -181,8 +181,12 @@ class ZenFolder extends MozTabbrowserTabGroup {
|
||||
}
|
||||
|
||||
get allItems() {
|
||||
return [...this.querySelector('.tab-group-container').children].filter(
|
||||
(child) => !child.classList.contains('zen-tab-group-start')
|
||||
return [...this.groupContainer.children].filter(
|
||||
(child) =>
|
||||
!(
|
||||
child.classList.contains('zen-tab-group-start') ||
|
||||
child.classList.contains('pinned-tabs-container-separator')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -274,4 +278,4 @@ class ZenFolder extends MozTabbrowserTabGroup {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('zen-folder', ZenFolder);
|
||||
customElements.define('zen-folder', nsZenFolder);
|
||||
|
||||
@@ -33,8 +33,6 @@ function formatRelativeTime(timestamp) {
|
||||
|
||||
class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
#ZEN_MAX_SUBFOLDERS = Services.prefs.getIntPref('zen.folders.max-subfolders', 5);
|
||||
#ZEN_EDGE_ZONE_THRESHOLD =
|
||||
Services.prefs.getIntPref('zen.view.drag-and-drop.edge-zone-threshold', 25) / 100;
|
||||
|
||||
#popup = null;
|
||||
#popupTimer = null;
|
||||
@@ -189,6 +187,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
window.addEventListener('TabSelect', this);
|
||||
window.addEventListener('TabOpen', this);
|
||||
const onNewFolder = this.#onNewFolder.bind(this);
|
||||
document.getElementById('zen-context-menu-new-folder').addEventListener('command', onNewFolder);
|
||||
document
|
||||
.getElementById('zen-context-menu-new-folder-toolbar')
|
||||
.addEventListener('command', onNewFolder);
|
||||
@@ -803,6 +802,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
if (!isTab && !groupElem?.hasAttribute('selected') && !forCollapse) {
|
||||
groupElem = null; // Don't indent if the group is not selected
|
||||
}
|
||||
if (groupElem?.tagName.toLowerCase() === 'zen-workspace-collapsible-pins') {
|
||||
groupElem = null; // Don't indent if it's inside the collapsible pinned tabs
|
||||
}
|
||||
let level = groupElem?.level + 1 || 0;
|
||||
if (gBrowser.isTabGroupLabel(groupElem)) {
|
||||
// If it is a group label, we should not increase its level by one.
|
||||
@@ -1036,8 +1038,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
default: {
|
||||
// Should insert after zen-empty-tab
|
||||
const start =
|
||||
parentWorkingData.node.querySelector('.zen-tab-group-start').nextElementSibling;
|
||||
const start = parentWorkingData.node.groupStartElement.nextElementSibling;
|
||||
start.after(node);
|
||||
}
|
||||
}
|
||||
@@ -1062,18 +1063,16 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
* @param {Array<MozTabbrowserTab>|null} movingTabs The tabs being moved.
|
||||
*/
|
||||
highlightGroupOnDragOver(folder, movingTabs) {
|
||||
if (folder === this.#lastHighlightedGroup) return;
|
||||
if (folder === this.#lastHighlightedGroup) return true;
|
||||
const tab = movingTabs ? movingTabs[0] : null;
|
||||
if (this.#lastHighlightedGroup && this.#lastHighlightedGroup !== folder) {
|
||||
this.#lastHighlightedGroup.removeAttribute('selected');
|
||||
if (this.#lastHighlightedGroup.collapsed) {
|
||||
this.updateFolderIcon(this.#lastHighlightedGroup, 'close');
|
||||
}
|
||||
this.#lastHighlightedGroup = null;
|
||||
}
|
||||
|
||||
if (
|
||||
folder &&
|
||||
folder?.isZenFolder &&
|
||||
(!folder.hasAttribute('split-view-group') || !folder.hasAttribute('selected')) &&
|
||||
folder !== tab?.group &&
|
||||
!(
|
||||
@@ -1081,13 +1080,13 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
movingTabs?.some((t) => gBrowser.isTabGroupLabel(t))
|
||||
)
|
||||
) {
|
||||
folder.setAttribute('selected', 'true');
|
||||
folder.style.transform = '';
|
||||
if (folder.collapsed) {
|
||||
this.updateFolderIcon(folder, 'open');
|
||||
}
|
||||
this.#lastHighlightedGroup = folder;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1100,55 +1099,6 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the dragover logic when dragging a tab or tab group label over another tab group label.
|
||||
* This function determines where the dragged item should be visually dropped (before/after the group, or inside it)
|
||||
* and updates related styling and highlighting.
|
||||
*
|
||||
* @param {MozTabbrowserTabGroupLabel} currentDropElement The tab group label currently being dragged over.
|
||||
* @param {MozTabbrowserTab|MozTabbrowserTabGroupLabel} draggedTab The tab or tab group label being dragged.
|
||||
* @param {number} overlapPercent The percentage of overlap between the dragged item and the drop target.
|
||||
* @param {Array<MozTabbrowserTab>} movingTabs An array of tabs that are currently being dragged together.
|
||||
* @param {boolean} currentDropBefore Indicates if the current drop position is before the middle of the drop element.
|
||||
* @param {string|undefined} currentColorCode The current color code for dragover highlighting.
|
||||
* @returns {{dropElement: MozTabbrowserTabGroup|MozTabbrowserTab|MozTabbrowserTabGroupLabel, colorCode: string|undefined, dropBefore: boolean}}
|
||||
* An object containing the updated drop element, color code for highlighting, and drop position.
|
||||
*/
|
||||
handleDragOverTabGroupLabel(
|
||||
currentDropElement,
|
||||
draggedTab,
|
||||
overlapPercent,
|
||||
movingTabs,
|
||||
currentDropBefore,
|
||||
currentColorCode
|
||||
) {
|
||||
let dropElement = currentDropElement;
|
||||
let dropBefore = currentDropBefore;
|
||||
let colorCode = currentColorCode;
|
||||
|
||||
const dropElementGroup = dropElement?.isZenFolder ? dropElement : dropElement?.group;
|
||||
const isSplitGroup = dropElement?.group?.hasAttribute('split-view-group');
|
||||
let firstGroupElem =
|
||||
dropElementGroup?.querySelector('.zen-tab-group-start')?.nextElementSibling;
|
||||
if (gBrowser.isTabGroup(firstGroupElem)) firstGroupElem = firstGroupElem.labelElement;
|
||||
|
||||
const isInMiddleZone =
|
||||
overlapPercent >= this.#ZEN_EDGE_ZONE_THRESHOLD &&
|
||||
overlapPercent <= 1 - this.#ZEN_EDGE_ZONE_THRESHOLD;
|
||||
const shouldDropInside = isInMiddleZone && !isSplitGroup;
|
||||
|
||||
if (shouldDropInside) {
|
||||
dropElement = firstGroupElem;
|
||||
dropBefore = true;
|
||||
this.highlightGroupOnDragOver(dropElementGroup, movingTabs);
|
||||
} else {
|
||||
colorCode = undefined;
|
||||
this.highlightGroupOnDragOver(null);
|
||||
}
|
||||
|
||||
return { dropElement, colorCode, dropBefore };
|
||||
}
|
||||
|
||||
#normalizeGroupItems(items) {
|
||||
return items
|
||||
.filter((item) => !item.hasAttribute('zen-empty-tab'))
|
||||
@@ -1212,6 +1162,11 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
return heightShift;
|
||||
} else {
|
||||
heightShift += window.windowUtils.getBoundsWithoutFlushing(tabsContainer).height;
|
||||
if (tabsContainer.separatorElement) {
|
||||
heightShift -= window.windowUtils.getBoundsWithoutFlushing(
|
||||
tabsContainer.separatorElement
|
||||
).height;
|
||||
}
|
||||
}
|
||||
return heightShift;
|
||||
}
|
||||
@@ -1225,8 +1180,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
const activeFoldersIds = new Set();
|
||||
const itemsToHide = [];
|
||||
|
||||
const tabsContainer = group.querySelector('.tab-group-container');
|
||||
const groupStart = group.querySelector('.zen-tab-group-start');
|
||||
const tabsContainer = group.groupContainer;
|
||||
const groupStart = group.groupStartElement;
|
||||
|
||||
const groupItems = this.#collectGroupItems(group, {
|
||||
selectedTabs,
|
||||
@@ -1304,11 +1259,11 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
const animations = [];
|
||||
const itemsToHide = [];
|
||||
|
||||
const tabsContainer = group.querySelector('.tab-group-container');
|
||||
const tabsContainer = group.groupContainer;
|
||||
tabsContainer.removeAttribute('hidden');
|
||||
tabsContainer.style.overflow = 'hidden';
|
||||
|
||||
const groupStart = group.querySelector('.zen-tab-group-start');
|
||||
const groupStart = group.groupStartElement;
|
||||
const itemsToShow = this.#normalizeGroupItems(group.childGroupsAndTabs);
|
||||
const activeFolders = group.childActiveGroups;
|
||||
|
||||
@@ -1422,7 +1377,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
folder.removeAttribute('has-active');
|
||||
folder.activeTabs = [];
|
||||
const groupItems = this.#normalizeGroupItems(folder.allItems);
|
||||
const tabsContainer = folder.querySelector('.tab-group-container');
|
||||
const tabsContainer = folder.groupContainer;
|
||||
|
||||
// Set correct margin-top after animation
|
||||
const afterAnimate = () => {
|
||||
@@ -1436,7 +1391,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
groupStart.style.marginTop = `${-(collapsedHeight + 4)}px`;
|
||||
};
|
||||
|
||||
const groupStart = folder.querySelector('.zen-tab-group-start');
|
||||
const groupStart = folder.groupStartElement;
|
||||
const collapsedHeight = this.#calculateHeightShift(tabsContainer, []);
|
||||
|
||||
// Collect animations for this specific folder becoming inactive
|
||||
@@ -1474,7 +1429,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
animations.push(async () => {
|
||||
folder.removeAttribute('has-active');
|
||||
const groupItems = this.#normalizeGroupItems(folder.allItems);
|
||||
const tabsContainer = folder.querySelector('.tab-group-container');
|
||||
const tabsContainer = folder.groupContainer;
|
||||
|
||||
// Set correct margin-top after animation
|
||||
const afterAnimate = () => {
|
||||
@@ -1488,7 +1443,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
groupStart.style.marginTop = `${-(collapsedHeight + 4)}px`;
|
||||
};
|
||||
|
||||
const groupStart = folder.querySelector('.zen-tab-group-start');
|
||||
const groupStart = folder.groupStartElement;
|
||||
const collapsedHeight = this.#calculateHeightShift(tabsContainer, []);
|
||||
|
||||
// Collect animations for this specific folder becoming inactive
|
||||
@@ -1573,8 +1528,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
currentGroup.activeTabs = activeTabs;
|
||||
}
|
||||
|
||||
const tabsContainer = currentGroup.querySelector('.tab-group-container');
|
||||
const groupStart = currentGroup.querySelector('.zen-tab-group-start');
|
||||
const tabsContainer = currentGroup.groupContainer;
|
||||
const groupStart = currentGroup.groupStartElement;
|
||||
tabsContainer.style.overflow = 'clip';
|
||||
|
||||
if (tabsContainer.hasAttribute('hidden')) tabsContainer.removeAttribute('hidden');
|
||||
@@ -1673,8 +1628,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
|
||||
animateGroupMove(group, expand = false) {
|
||||
if (!group?.isZenFolder) return;
|
||||
const groupStart = group.querySelector('.zen-tab-group-start');
|
||||
const tabsContainer = group.querySelector('.tab-group-container');
|
||||
const groupStart = group.groupStartElement;
|
||||
const tabsContainer = group.groupContainer;
|
||||
const heightContainer = expand ? 0 : this.#calculateHeightShift(tabsContainer, []);
|
||||
tabsContainer.style.overflow = 'clip';
|
||||
|
||||
|
||||
@@ -202,16 +202,6 @@ zen-folder {
|
||||
}
|
||||
}
|
||||
|
||||
&[collapsed] {
|
||||
& > .tabbrowser-tab:not([hidden]) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&:not([has-active]) > .tab-group-container {
|
||||
overflow-y: clip;
|
||||
}
|
||||
}
|
||||
|
||||
:root[zen-sidebar-expanded] &[has-active] > .tab-group-label-container {
|
||||
& .tab-reset-button {
|
||||
display: flex;
|
||||
@@ -224,6 +214,11 @@ zen-folder {
|
||||
}
|
||||
}
|
||||
|
||||
zen-workspace[collapsedpinnedtabs] .zen-workspace-pinned-tabs-section,
|
||||
zen-folder[collapsed]:not([has-active]) > .tab-group-container {
|
||||
overflow-y: clip;
|
||||
}
|
||||
|
||||
/* Tabs popup */
|
||||
#zen-folder-tabs-popup {
|
||||
--arrowpanel-padding: 0;
|
||||
|
||||
@@ -8,6 +8,7 @@ EXTRA_PP_COMPONENTS += [
|
||||
|
||||
DIRS += [
|
||||
"common",
|
||||
"drag-and-drop",
|
||||
"glance",
|
||||
"mods",
|
||||
"tests",
|
||||
|
||||
@@ -464,6 +464,12 @@ class nsZenWindowSync {
|
||||
}
|
||||
const relativeTab = this.#getItemFromWindow(aWindow, originalSibling.id);
|
||||
if (relativeTab) {
|
||||
gBrowser.tabContainer.tabDragAndDrop.handle_drop_transition(
|
||||
relativeTab,
|
||||
aTargetItem,
|
||||
[aTargetItem],
|
||||
false
|
||||
);
|
||||
relativeTab.after(aTargetItem);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -228,9 +228,26 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
#getDragImageForSplit(tab) {
|
||||
const element = window.MozXULElement.parseXULToFragment(
|
||||
`
|
||||
<vbox id="zen-split-view-drag-image">
|
||||
<image />
|
||||
<label />
|
||||
</vbox>
|
||||
`
|
||||
).querySelector('#zen-split-view-drag-image');
|
||||
const image = element.querySelector('image');
|
||||
const label = element.querySelector('label');
|
||||
image.src = tab.getAttribute('image');
|
||||
label.textContent = tab.label;
|
||||
document.documentElement.appendChild(element);
|
||||
this._dndElement = element;
|
||||
return element;
|
||||
}
|
||||
|
||||
onBrowserDragOverToSplit(event) {
|
||||
if (this.fakeBrowser) {
|
||||
this.onBrowserDragEndToSplit(event);
|
||||
return;
|
||||
}
|
||||
var dt = event.dataTransfer;
|
||||
@@ -239,23 +256,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
// tab copy or move
|
||||
draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
// not our drop then
|
||||
if (!draggedTab || gBrowser.selectedTab.hasAttribute('zen-empty-tab')) {
|
||||
if (!gBrowser.isTab(draggedTab) || gBrowser.selectedTab.hasAttribute('zen-empty-tab')) {
|
||||
return;
|
||||
}
|
||||
gBrowser.tabContainer.tabDragAndDrop.finishMoveTogetherSelectedTabs(draggedTab);
|
||||
}
|
||||
if (
|
||||
!draggedTab ||
|
||||
this._canDrop ||
|
||||
this._hasAnimated ||
|
||||
this.fakeBrowser ||
|
||||
!this._lastOpenedTab ||
|
||||
(this._lastOpenedTab &&
|
||||
this._lastOpenedTab.getAttribute('zen-workspace-id') !==
|
||||
draggedTab.getAttribute('zen-workspace-id') &&
|
||||
!this._lastOpenedTab.hasAttribute('zen-essential')) ||
|
||||
draggedTab === this._lastOpenedTab
|
||||
(this._lastOpenedTab.getAttribute('zen-workspace-id') !==
|
||||
draggedTab.getAttribute('zen-workspace-id') &&
|
||||
!this._lastOpenedTab.hasAttribute('zen-essential'))
|
||||
) {
|
||||
this._lastOpenedTab = gBrowser.selectedTab;
|
||||
}
|
||||
if (!draggedTab || this._canDrop || this._hasAnimated || this.fakeBrowser) {
|
||||
return;
|
||||
}
|
||||
if (draggedTab.splitView) {
|
||||
@@ -285,19 +299,25 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
return;
|
||||
}
|
||||
dt.mozCursor = 'default';
|
||||
if (!this._dndElement) {
|
||||
const originalDNDArgs = gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs;
|
||||
requestAnimationFrame(() => {
|
||||
dt.updateDragImage(
|
||||
this.#getDragImageForSplit(draggedTab),
|
||||
originalDNDArgs[1],
|
||||
originalDNDArgs[2]
|
||||
);
|
||||
});
|
||||
gBrowser.tabContainer.tabDragAndDrop.clearDragOverVisuals();
|
||||
}
|
||||
const oldTab = this._lastOpenedTab;
|
||||
this._canDrop = true;
|
||||
Services.zen.playHapticFeedback();
|
||||
{
|
||||
this._draggingTab = draggedTab;
|
||||
gBrowser.selectedTab = oldTab;
|
||||
this._hasAnimated = true;
|
||||
this.tabBrowserPanel.setAttribute('dragging-split', 'true');
|
||||
for (const tab of gBrowser.tabs) {
|
||||
tab.style.removeProperty('transform');
|
||||
if (tab.group) {
|
||||
tab.group.style.removeProperty('transform');
|
||||
}
|
||||
}
|
||||
// Add a min width to all the browser elements to prevent them from resizing
|
||||
const panelsWidth = gBrowser.tabbox.getBoundingClientRect().width;
|
||||
let numOfTabsToDivide = 2;
|
||||
@@ -330,11 +350,6 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
this.fakeBrowser.setAttribute('has-split-view', 'true');
|
||||
}
|
||||
gBrowser.tabbox.appendChild(this.fakeBrowser);
|
||||
this.fakeBrowser.style.setProperty(
|
||||
'--zen-split-view-fake-icon',
|
||||
`url(${draggedTab.getAttribute('image')})`
|
||||
);
|
||||
draggedTab._visuallySelected = true;
|
||||
this.fakeBrowser.setAttribute('side', side);
|
||||
this._finishAllAnimatingPromise = Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
@@ -371,13 +386,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
]);
|
||||
if (this._finishAllAnimatingPromise) {
|
||||
this._finishAllAnimatingPromise.then(() => {
|
||||
draggedTab.linkedBrowser.docShellIsActive = false;
|
||||
draggedTab.linkedBrowser
|
||||
.closest('.browserSidebarContainer')
|
||||
.classList.remove('deck-selected');
|
||||
if (draggedTab !== oldTab) {
|
||||
draggedTab.linkedBrowser.docShellIsActive = false;
|
||||
draggedTab.linkedBrowser
|
||||
.closest('.browserSidebarContainer')
|
||||
.classList.remove('deck-selected');
|
||||
}
|
||||
this.fakeBrowser.addEventListener('dragleave', this.onBrowserDragEndToSplit);
|
||||
this._canDrop = true;
|
||||
draggedTab._visuallySelected = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -390,12 +406,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
const panelsRect = gBrowser.tabbox.getBoundingClientRect();
|
||||
const fakeBrowserRect = this.fakeBrowser && this.fakeBrowser.getBoundingClientRect();
|
||||
if (
|
||||
((event.target.closest('#tabbrowser-tabbox') && event.target != this.fakeBrowser) ||
|
||||
(fakeBrowserRect &&
|
||||
event.clientX > fakeBrowserRect.left &&
|
||||
event.clientX < fakeBrowserRect.left + fakeBrowserRect.width &&
|
||||
event.clientY > fakeBrowserRect.top &&
|
||||
event.clientY < fakeBrowserRect.top + fakeBrowserRect.height) ||
|
||||
((fakeBrowserRect &&
|
||||
event.clientX > fakeBrowserRect.left &&
|
||||
event.clientX < fakeBrowserRect.left + fakeBrowserRect.width &&
|
||||
event.clientY > fakeBrowserRect.top &&
|
||||
event.clientY < fakeBrowserRect.top + fakeBrowserRect.height) ||
|
||||
(event.screenX === 0 && event.screenY === 0)) && // It's equivalent to 0 if the event has been dropped
|
||||
!cancelled
|
||||
) {
|
||||
@@ -415,50 +430,47 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
if (!this.fakeBrowser) {
|
||||
return;
|
||||
}
|
||||
this.fakeBrowser.classList.add('fade-out');
|
||||
const side = this.fakeBrowser.getAttribute('side');
|
||||
if (this._draggingTab) this._draggingTab.setAttribute('zen-has-splitted', 'true');
|
||||
this._lastOpenedTab = gBrowser.selectedTab;
|
||||
this._draggingTab = null;
|
||||
try {
|
||||
this._canDrop = false;
|
||||
Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
gBrowser.tabbox,
|
||||
side === 'left'
|
||||
gBrowser.tabContainer.tabDragAndDrop.clearSpaceSwitchTimer();
|
||||
event.dataTransfer.updateDragImage(
|
||||
...gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs
|
||||
);
|
||||
this._canDrop = false;
|
||||
Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
gBrowser.tabbox,
|
||||
side === 'left'
|
||||
? {
|
||||
paddingLeft: [`${halfWidth}px`, 0],
|
||||
}
|
||||
: {
|
||||
paddingRight: [`${halfWidth}px`, 0],
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: 'ease-out',
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.fakeBrowser,
|
||||
{
|
||||
width: [`${halfWidth - padding * 2}px`, 0],
|
||||
...(side === 'left'
|
||||
? {
|
||||
paddingLeft: [`${halfWidth}px`, 0],
|
||||
marginLeft: [`${-halfWidth}px`, 0],
|
||||
}
|
||||
: {
|
||||
paddingRight: [`${halfWidth}px`, 0],
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: 'ease-out',
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.fakeBrowser,
|
||||
{
|
||||
width: [`${halfWidth - padding * 2}px`, 0],
|
||||
...(side === 'left'
|
||||
? {
|
||||
marginLeft: [`${-halfWidth}px`, 0],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: 'ease-out',
|
||||
}
|
||||
),
|
||||
]).then(() => {
|
||||
this._maybeRemoveFakeBrowser();
|
||||
});
|
||||
} catch {
|
||||
this._canDrop = false;
|
||||
: {}),
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: 'ease-out',
|
||||
}
|
||||
),
|
||||
]).finally(() => {
|
||||
this._maybeRemoveFakeBrowser();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1667,11 +1679,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
_maybeRemoveFakeBrowser(select = true) {
|
||||
gBrowser.tabbox.removeAttribute('style');
|
||||
this.tabBrowserPanel.removeAttribute('dragging-split');
|
||||
if (this._dndElement) {
|
||||
this._dndElement.remove();
|
||||
delete this._dndElement;
|
||||
}
|
||||
if (this.fakeBrowser) {
|
||||
delete this._hasAnimated;
|
||||
this.fakeBrowser.remove();
|
||||
this.fakeBrowser = null;
|
||||
if (this._draggingTab) this._draggingTab._visuallySelected = false;
|
||||
if (select) {
|
||||
gBrowser.selectedTab = this._draggingTab;
|
||||
this._draggingTab = null;
|
||||
@@ -1728,6 +1743,12 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
return false;
|
||||
}
|
||||
|
||||
const droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(gBrowser.getTabForBrowser(browser));
|
||||
if (droppedOnTab === this._draggingTab) {
|
||||
this.createEmptySplit(dropSide == 'right');
|
||||
return true;
|
||||
}
|
||||
|
||||
gBrowser.selectedTab = this._draggingTab;
|
||||
this._draggingTab = null;
|
||||
const browserContainer = draggedTab.linkedBrowser?.closest('.browserSidebarContainer');
|
||||
@@ -1735,7 +1756,6 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
browserContainer.style.opacity = '0';
|
||||
}
|
||||
|
||||
const droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(gBrowser.getTabForBrowser(browser));
|
||||
if (droppedOnTab && droppedOnTab !== draggedTab) {
|
||||
// Calculate which side of the target browser the drop occurred
|
||||
// const browserRect = browser.getBoundingClientRect();
|
||||
@@ -1977,13 +1997,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
createEmptySplit() {
|
||||
createEmptySplit(rightSide = true) {
|
||||
const selectedTab = gBrowser.selectedTab;
|
||||
const emptyTab = gZenWorkspaces._emptyTab;
|
||||
let tabs = rightSide ? [selectedTab, emptyTab] : [emptyTab, selectedTab];
|
||||
const data = {
|
||||
tabs: [selectedTab, emptyTab],
|
||||
tabs: tabs,
|
||||
gridType: 'grid',
|
||||
layoutTree: this.calculateLayoutTree([selectedTab, emptyTab], 'grid'),
|
||||
layoutTree: this.calculateLayoutTree(tabs, 'grid'),
|
||||
};
|
||||
this._data.push(data);
|
||||
this.activateSplitView(data);
|
||||
@@ -2016,7 +2037,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
this.removeTabFromGroup(emptyTab, groupIndex, { forUnsplit: true });
|
||||
gBrowser.selectedTab = selectedTab;
|
||||
this.resetTabState(emptyTab, false);
|
||||
this.splitTabs([selectedTab, newSelectedTab], 'grid', 1);
|
||||
this.splitTabs(
|
||||
rightSide ? [selectedTab, newSelectedTab] : [newSelectedTab, selectedTab],
|
||||
'grid',
|
||||
rightSide ? 1 : 0
|
||||
);
|
||||
} else {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@@ -210,31 +210,28 @@
|
||||
right: var(--zen-element-separation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 3.5rem;
|
||||
pointer-events: none;
|
||||
height: 3.5rem;
|
||||
background: var(--zen-split-view-fake-icon);
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s;
|
||||
transition-delay: 0.1s;
|
||||
#zen-split-view-drag-image {
|
||||
width: 200px;
|
||||
height: 250px;
|
||||
border-radius: 16px;
|
||||
background: black;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
|
||||
@starting-style {
|
||||
opacity: 0;
|
||||
}
|
||||
& image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&.fade-out::after {
|
||||
opacity: 0;
|
||||
transition-delay: 0s;
|
||||
& label {
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,12 +84,16 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
|
||||
onTabIconChanged(tab, url = null) {
|
||||
tab.dispatchEvent(new CustomEvent('ZenTabIconChanged', { bubbles: true, detail: { tab } }));
|
||||
const iconUrl = url ?? tab.iconImage.src;
|
||||
if (tab.hasAttribute('zen-essential')) {
|
||||
tab.style.setProperty('--zen-essential-tab-icon', `url(${iconUrl})`);
|
||||
this.setEssentialTabIcon(tab, url);
|
||||
}
|
||||
}
|
||||
|
||||
setEssentialTabIcon(tab, url = null) {
|
||||
const iconUrl = url ?? tab.getAttribute('image') ?? '';
|
||||
tab.style.setProperty('--zen-essential-tab-icon', `url(${iconUrl})`);
|
||||
}
|
||||
|
||||
_onTabResetPinButton(event, tab) {
|
||||
event.stopPropagation();
|
||||
this._resetTabToStoredState(tab);
|
||||
@@ -322,6 +326,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
const state = this.#getTabState(tab);
|
||||
|
||||
const initialState = tab._zenPinnedInitialState;
|
||||
if (!initialState?.entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove everything except the entry we want to keep
|
||||
state.entries = [initialState.entry];
|
||||
@@ -531,15 +538,16 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
|
||||
moveToAnotherTabContainerIfNecessary(event, movingTabs) {
|
||||
movingTabs = [...movingTabs];
|
||||
if (!this.enabled) {
|
||||
return false;
|
||||
}
|
||||
movingTabs = [...movingTabs];
|
||||
try {
|
||||
const pinnedTabsTarget =
|
||||
event.target.closest('.zen-current-workspace-indicator') || this._isGoingToPinnedTabs;
|
||||
const pinnedTabsTarget = event.target.closest(
|
||||
':is(.zen-current-workspace-indicator, .zen-workspace-pinned-tabs-section)'
|
||||
);
|
||||
const essentialTabsTarget = event.target.closest('.zen-essentials-container');
|
||||
const tabsTarget = !this._isGoingToPinnedTabs;
|
||||
const tabsTarget = !pinnedTabsTarget;
|
||||
|
||||
// TODO: Solve the issue of adding a tab between two groups
|
||||
// Remove group labels from the moving tabs and replace it
|
||||
@@ -682,9 +690,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
|
||||
removeTabContainersDragoverClass(hideIndicator = true) {
|
||||
if (this._dragIndicator) {
|
||||
Services.zen.playHapticFeedback();
|
||||
}
|
||||
this.dragIndicator.remove();
|
||||
this._dragIndicator = null;
|
||||
if (hideIndicator) {
|
||||
@@ -692,95 +697,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
onDragFinish() {
|
||||
for (const item of this.dragShiftableItems) {
|
||||
item.style.transform = '';
|
||||
}
|
||||
delete this._topToNormalTabs;
|
||||
for (const item of gBrowser.tabContainer.ariaFocusableItems) {
|
||||
if (gBrowser.isTab(item)) {
|
||||
let isVisible = true;
|
||||
let parent = item.group;
|
||||
while (parent) {
|
||||
if (!parent.visible) {
|
||||
isVisible = false;
|
||||
break;
|
||||
}
|
||||
parent = parent.group;
|
||||
}
|
||||
if (!isVisible) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const itemToAnimate =
|
||||
item.group?.hasAttribute('split-view-group') || gBrowser.isTabGroupLabel(item)
|
||||
? item.group
|
||||
: item;
|
||||
itemToAnimate.style.removeProperty('--zen-folder-indent');
|
||||
}
|
||||
this.removeTabContainersDragoverClass();
|
||||
}
|
||||
|
||||
get dragShiftableItems() {
|
||||
const separator = gZenWorkspaces.pinnedTabsContainer.querySelector(
|
||||
'.pinned-tabs-container-separator'
|
||||
);
|
||||
// Make sure to always return the separator at the start of the array
|
||||
return Services.prefs.getBoolPref('zen.view.show-newtab-button-top')
|
||||
? [separator, gZenWorkspaces.activeWorkspaceElement.newTabButton]
|
||||
: [separator];
|
||||
}
|
||||
|
||||
animateSeparatorMove(movingTabs, dropElement, isPinned) {
|
||||
let draggedTab = movingTabs[0];
|
||||
if (gBrowser.isTabGroupLabel(draggedTab) && draggedTab.group.isZenFolder) {
|
||||
this._isGoingToPinnedTabs = true;
|
||||
return;
|
||||
}
|
||||
if (draggedTab?.group?.hasAttribute('split-view-group')) {
|
||||
draggedTab = draggedTab.group;
|
||||
}
|
||||
const itemsToCheck = this.dragShiftableItems;
|
||||
let translate = movingTabs[isPinned ? movingTabs.length - 1 : 0].getBoundingClientRect().top;
|
||||
if (isPinned) {
|
||||
const rect = draggedTab.getBoundingClientRect();
|
||||
translate += rect.height;
|
||||
}
|
||||
const draggingTabHeight = movingTabs.reduce((acc, item) => {
|
||||
return acc + window.windowUtils.getBoundsWithoutFlushing(item).height;
|
||||
}, 0);
|
||||
if (typeof this._topToNormalTabs === 'undefined') {
|
||||
const rects = itemsToCheck.map((item) => window.windowUtils.getBoundsWithoutFlushing(item));
|
||||
this._topToNormalTabs = rects[0].top + rects.at(-1).height / (isPinned ? 2 : 4);
|
||||
}
|
||||
let topToNormalTabs = this._topToNormalTabs;
|
||||
const isGoingToPinnedTabs =
|
||||
translate < topToNormalTabs && gBrowser.pinnedTabCount - gBrowser._numZenEssentials > 0;
|
||||
const multiplier = isGoingToPinnedTabs !== isPinned ? (isGoingToPinnedTabs ? 1 : -1) : 0;
|
||||
this._isGoingToPinnedTabs = isGoingToPinnedTabs;
|
||||
if (!dropElement) {
|
||||
itemsToCheck.forEach((item) => {
|
||||
item.style.transform = `translateY(${draggingTabHeight * multiplier}px)`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getLastTabBound(lastBound, lastTab, isDraggingFolder = false) {
|
||||
if (!lastTab.pinned || isDraggingFolder) {
|
||||
return lastBound;
|
||||
}
|
||||
const shiftedItems = this.dragShiftableItems;
|
||||
let totalHeight = shiftedItems.reduce((acc, item) => {
|
||||
return acc + window.windowUtils.getBoundsWithoutFlushing(item).height;
|
||||
}, 0);
|
||||
if (shiftedItems.length === 1) {
|
||||
// Means the new tab button is not at the top or not visible
|
||||
const lastTabRect = window.windowUtils.getBoundsWithoutFlushing(lastTab);
|
||||
totalHeight += lastTabRect.height;
|
||||
}
|
||||
return lastBound + totalHeight + 6;
|
||||
}
|
||||
|
||||
get dragIndicator() {
|
||||
if (!this._dragIndicator) {
|
||||
this._dragIndicator = document.createElement('div');
|
||||
|
||||
@@ -715,7 +715,6 @@
|
||||
}
|
||||
|
||||
& .zen-essentials-container {
|
||||
will-change: transform;
|
||||
justify-content: center;
|
||||
grid-template-columns: 1fr !important;
|
||||
padding: 0 !important;
|
||||
@@ -1108,8 +1107,6 @@
|
||||
}
|
||||
|
||||
.zen-essentials-container {
|
||||
will-change: transform;
|
||||
|
||||
overflow: hidden;
|
||||
gap: 4px;
|
||||
transition:
|
||||
@@ -1166,7 +1163,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.zen-essentials-container > .tabbrowser-tab,
|
||||
.tabbrowser-tab[zen-essential='true'],
|
||||
#zen-welcome-initial-essentials-browser-sidebar-essentials .tabbrowser-tab {
|
||||
--toolbarbutton-inner-padding: 0;
|
||||
max-width: unset;
|
||||
@@ -1288,9 +1285,9 @@
|
||||
width: calc(var(--indicator-width) - 2 * var(--zen-drag-indicator-height) - 4px);
|
||||
height: var(--zen-drag-indicator-height);
|
||||
transition:
|
||||
top 0.1s ease-out,
|
||||
left 0.1s ease-out,
|
||||
width 0.1s ease-out;
|
||||
top 0.05s ease-out,
|
||||
left 0.05s ease-out,
|
||||
width 0.05s ease-out;
|
||||
|
||||
&::before {
|
||||
left: calc(-2 * var(--zen-drag-indicator-height));
|
||||
@@ -1373,7 +1370,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tabbrowser-tab[zen-dragtarget],
|
||||
.tab-group-label-container[zen-dragtarget] {
|
||||
z-index: 9 !important;
|
||||
/* Drag and drop */
|
||||
|
||||
#zen-dragover-background {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
/* Extra width to cover the sidebar splitter */
|
||||
width: calc(100% + var(--zen-toolbox-padding));
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
background: var(--zen-primary-color);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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/.
|
||||
/* 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/. */
|
||||
|
||||
import { nsZenMultiWindowFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
|
||||
|
||||
|
||||
@@ -1,17 +1,59 @@
|
||||
// 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/.
|
||||
/* 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/. */
|
||||
|
||||
import { nsZenFolder } from 'chrome://browser/content/zen-components/ZenFolder.mjs';
|
||||
|
||||
// A helper class to manage collapsible pinned tabs in a workspace.
|
||||
class nsZenCollapsiblePins extends nsZenFolder {
|
||||
#spaceElement;
|
||||
|
||||
connectedCallback() {
|
||||
this.setAttribute('hidden', 'true');
|
||||
this.#spaceElement = this.parentElement;
|
||||
super.connectedCallback();
|
||||
}
|
||||
|
||||
get groupContainer() {
|
||||
return this.#spaceElement.pinnedTabsContainer;
|
||||
}
|
||||
|
||||
get groupStartElement() {
|
||||
// Fetch this instead of the tab-group-start since it is not guaranteed this
|
||||
// element will be the first child of the pinned tabs container.
|
||||
return this.#spaceElement.pinnedTabsContainer.querySelector('.space-fake-collapsible-start');
|
||||
}
|
||||
|
||||
get collapsed() {
|
||||
return super.collapsed;
|
||||
}
|
||||
|
||||
set collapsed(value) {
|
||||
if (value) {
|
||||
this.#spaceElement.setAttribute('collapsedpinnedtabs', 'true');
|
||||
} else {
|
||||
this.#spaceElement.removeAttribute('collapsedpinnedtabs');
|
||||
}
|
||||
super.collapsed = value;
|
||||
}
|
||||
}
|
||||
|
||||
export class nsZenWorkspace extends MozXULElement {
|
||||
#initialPinnedElementChildrenCount;
|
||||
|
||||
class nsZenWorkspace extends MozXULElement {
|
||||
static get markup() {
|
||||
return `
|
||||
<vbox class="zen-workspace-tabs-section zen-current-workspace-indicator" flex="1" context="zenWorkspaceMoreActions">
|
||||
<hbox class="zen-current-workspace-indicator-icon" />
|
||||
<vbox class="zen-workspace-tabs-section zen-current-workspace-indicator zen-drop-target" flex="1" context="zenWorkspaceMoreActions">
|
||||
<stack class="zen-current-workspace-indicator-stack">
|
||||
<image class="zen-current-workspace-indicator-chevron" />
|
||||
<hbox class="zen-current-workspace-indicator-icon" />
|
||||
</stack>
|
||||
<label class="zen-current-workspace-indicator-name" flex="1" />
|
||||
<toolbarbutton class="toolbarbutton-1 chromeclass-toolbar-additional zen-workspaces-actions" context="zenWorkspaceMoreActions" />
|
||||
</vbox>
|
||||
<arrowscrollbox orient="vertical" class="workspace-arrowscrollbox">
|
||||
<vbox class="zen-workspace-tabs-section zen-workspace-pinned-tabs-section" hide-separator="true">
|
||||
<html:div class="zen-tab-group-start space-fake-collapsible-start" style="order: -9999;" />
|
||||
<hbox class="pinned-tabs-container-separator">
|
||||
<toolbarseparator flex="1" />
|
||||
<toolbarbutton command="cmd_zenCloseUnpinnedTabs"
|
||||
@@ -68,6 +110,9 @@ class nsZenWorkspace extends MozXULElement {
|
||||
this.tabsContainer = this.querySelector('.zen-workspace-normal-tabs-section');
|
||||
this.indicator = this.querySelector('.zen-current-workspace-indicator');
|
||||
this.pinnedTabsContainer = this.querySelector('.zen-workspace-pinned-tabs-section');
|
||||
this.pinnedTabsContainer.separatorElement = this.pinnedTabsContainer.querySelector(
|
||||
'.pinned-tabs-container-separator'
|
||||
);
|
||||
this.initializeAttributeInheritance();
|
||||
|
||||
this.scrollbox = this.querySelector('arrowscrollbox');
|
||||
@@ -80,10 +125,17 @@ class nsZenWorkspace extends MozXULElement {
|
||||
this.scrollbox.addEventListener('underflow', this);
|
||||
this.scrollbox.addEventListener('overflow', this);
|
||||
|
||||
this.indicator.querySelector('.zen-current-workspace-indicator-name').onRenameFinished =
|
||||
this.onIndicatorRenameFinished.bind(this);
|
||||
const indicatorName = this.indicator.querySelector('.zen-current-workspace-indicator-name');
|
||||
indicatorName.onRenameFinished = this.onIndicatorRenameFinished.bind(this);
|
||||
indicatorName.addEventListener('dblclick', (event) => {
|
||||
if (this.hasPinnedTabs) {
|
||||
// Prevent renaming when there are pinned tabs
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
this.pinnedTabsContainer.scrollbox = this.scrollbox;
|
||||
this.#initialPinnedElementChildrenCount = this.pinnedTabsContainer.children.length;
|
||||
|
||||
this.indicator
|
||||
.querySelector('.zen-workspaces-actions')
|
||||
@@ -92,10 +144,20 @@ class nsZenWorkspace extends MozXULElement {
|
||||
this.indicator
|
||||
.querySelector('.zen-current-workspace-indicator-icon')
|
||||
.addEventListener('dblclick', (event) => {
|
||||
if (this.hasPinnedTabs) {
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
gZenWorkspaces.changeWorkspaceIcon();
|
||||
});
|
||||
|
||||
this.indicator.addEventListener('click', (event) => {
|
||||
if (this.hasPinnedTabs) {
|
||||
event.stopPropagation();
|
||||
this.collapsiblePins.collapsed = !this.collapsiblePins.collapsed;
|
||||
}
|
||||
});
|
||||
|
||||
if (!gZenWorkspaces.currentWindowIsSyncing) {
|
||||
let actionsButton = this.indicator.querySelector('.zen-workspaces-actions');
|
||||
const moveTabToFragment = window.MozXULElement.parseXULToFragment(
|
||||
@@ -169,11 +231,26 @@ class nsZenWorkspace extends MozXULElement {
|
||||
this.tabsContainer.setAttribute('zen-workspace-id', this.id);
|
||||
this.pinnedTabsContainer.setAttribute('zen-workspace-id', this.id);
|
||||
|
||||
this.collapsiblePins = document.createXULElement('zen-workspace-collapsible-pins');
|
||||
this.prepend(this.collapsiblePins);
|
||||
|
||||
this.#updateOverflow();
|
||||
|
||||
this.onGradientCacheChanged = this.#onGradientCacheChanged.bind(this);
|
||||
window.addEventListener('ZenGradientCacheChanged', this.onGradientCacheChanged);
|
||||
|
||||
const tabPinCallback = () => {
|
||||
this.checkPinsExistence();
|
||||
};
|
||||
|
||||
this.addEventListener('TabPinned', tabPinCallback);
|
||||
this.addEventListener('TabUnpinned', tabPinCallback);
|
||||
this.addEventListener('TabClose', (event) => {
|
||||
if (event.target.pinned) {
|
||||
tabPinCallback();
|
||||
}
|
||||
});
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('ZenWorkspaceAttached', {
|
||||
bubbles: true,
|
||||
@@ -185,6 +262,7 @@ class nsZenWorkspace extends MozXULElement {
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener('ZenGradientCacheChanged', this.onGradientCacheChanged);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
get active() {
|
||||
@@ -200,6 +278,14 @@ class nsZenWorkspace extends MozXULElement {
|
||||
this.#updateOverflow();
|
||||
}
|
||||
|
||||
get hasPinnedTabs() {
|
||||
return this.hasAttribute('haspinnedtabs');
|
||||
}
|
||||
|
||||
get hasCollapsedPinnedTabs() {
|
||||
return this.hasAttribute('collapsedpinnedtabs');
|
||||
}
|
||||
|
||||
#updateOverflow() {
|
||||
if (!this.scrollbox) return;
|
||||
if (this.overflows) {
|
||||
@@ -273,6 +359,15 @@ class nsZenWorkspace extends MozXULElement {
|
||||
this.style.setProperty('--zen-primary-color', primaryColor);
|
||||
}
|
||||
|
||||
checkPinsExistence() {
|
||||
if (this.pinnedTabsContainer.children.length > this.#initialPinnedElementChildrenCount) {
|
||||
this.setAttribute('haspinnedtabs', 'true');
|
||||
} else {
|
||||
this.removeAttribute('haspinnedtabs');
|
||||
this.collapsiblePins.collapsed = false;
|
||||
}
|
||||
}
|
||||
|
||||
clearThemeStyles() {
|
||||
this.style.colorScheme = '';
|
||||
this.style.removeProperty('--toolbox-textcolor');
|
||||
@@ -311,3 +406,4 @@ class nsZenWorkspace extends MozXULElement {
|
||||
}
|
||||
|
||||
customElements.define('zen-workspace', nsZenWorkspace);
|
||||
customElements.define('zen-workspace-collapsible-pins', nsZenCollapsiblePins);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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/.
|
||||
/* 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/. */
|
||||
|
||||
class nsZenWorkspaceCreation extends MozXULElement {
|
||||
#wasInCollapsedMode = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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/.
|
||||
/* 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/. */
|
||||
|
||||
class nsZenWorkspaceIcons extends MozXULElement {
|
||||
constructor() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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/.
|
||||
/* 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/. */
|
||||
|
||||
import { nsZenThemePicker } from 'chrome://browser/content/zen-components/ZenGradientGenerator.mjs';
|
||||
|
||||
@@ -465,6 +465,7 @@ class nsZenWorkspaces {
|
||||
workspaceWrapper.pinnedTabsContainer,
|
||||
tabs
|
||||
);
|
||||
workspaceWrapper.checkPinsExistence();
|
||||
resolve();
|
||||
},
|
||||
{ once: true }
|
||||
@@ -827,6 +828,20 @@ class nsZenWorkspaces {
|
||||
return [...this._workspaceCache];
|
||||
}
|
||||
|
||||
getWorkspacesForSessionStore() {
|
||||
const spaces = this.getWorkspaces();
|
||||
let spacesForSS = [];
|
||||
for (const space of spaces) {
|
||||
let newSpace = { ...space };
|
||||
const element = this.workspaceElement(space.uuid);
|
||||
if (element) {
|
||||
newSpace.hasCollapsedPinnedTabs = element.hasCollapsedPinnedTabs;
|
||||
}
|
||||
spacesForSS.push(newSpace);
|
||||
}
|
||||
return spacesForSS;
|
||||
}
|
||||
|
||||
async workspaceBookmarks() {
|
||||
if (this.privateWindowOrDisabled) {
|
||||
this._workspaceBookmarksCache = {
|
||||
@@ -854,11 +869,22 @@ class nsZenWorkspaces {
|
||||
if (this.#hasInitialized) {
|
||||
return;
|
||||
}
|
||||
this._workspaceCache = aWinData.spaces?.length
|
||||
? aWinData.spaces
|
||||
const spacesFromStore = aWinData.spaces || [];
|
||||
this._workspaceCache = spacesFromStore.length
|
||||
? [...spacesFromStore]
|
||||
: [await this.createAndSaveWorkspace('Space', undefined, true)];
|
||||
for (const workspace of this._workspaceCache) {
|
||||
// We don't want to depend on this by mistake
|
||||
delete workspace.hasCollapsedPinnedTabs;
|
||||
}
|
||||
this.activeWorkspace = aWinData.activeZenSpace || this._workspaceCache[0].uuid;
|
||||
await this.initializeWorkspaces();
|
||||
for (const workspace of spacesFromStore) {
|
||||
const element = this.workspaceElement(workspace.uuid);
|
||||
if (element) {
|
||||
element.collapsiblePins.collapsed = workspace.hasCollapsedPinnedTabs || false;
|
||||
}
|
||||
}
|
||||
this.#hasInitialized = true;
|
||||
}
|
||||
|
||||
@@ -1542,11 +1568,7 @@ class nsZenWorkspaces {
|
||||
}
|
||||
|
||||
async changeWorkspace(workspace, ...args) {
|
||||
if (
|
||||
!this.workspaceEnabled ||
|
||||
this.#inChangingWorkspace ||
|
||||
gNavToolbox.hasAttribute('movingtab')
|
||||
) {
|
||||
if (!this.workspaceEnabled || this.#inChangingWorkspace) {
|
||||
return;
|
||||
}
|
||||
this.#inChangingWorkspace = true;
|
||||
@@ -1788,11 +1810,14 @@ class nsZenWorkspaces {
|
||||
}
|
||||
const indicatorName = workspaceIndicator.querySelector('.zen-current-workspace-indicator-name');
|
||||
const indicatorIcon = workspaceIndicator.querySelector('.zen-current-workspace-indicator-icon');
|
||||
const iconStack = workspaceIndicator.querySelector('.zen-current-workspace-indicator-stack');
|
||||
|
||||
if (this.workspaceHasIcon(currentWorkspace)) {
|
||||
indicatorIcon.removeAttribute('no-icon');
|
||||
iconStack.removeAttribute('no-icon');
|
||||
} else {
|
||||
indicatorIcon.setAttribute('no-icon', 'true');
|
||||
iconStack.setAttribute('no-icon', 'true');
|
||||
}
|
||||
const icon = this.getWorkspaceIcon(currentWorkspace);
|
||||
indicatorIcon.innerHTML = '';
|
||||
@@ -2404,7 +2429,7 @@ class nsZenWorkspaces {
|
||||
return workspaceData;
|
||||
}
|
||||
|
||||
async updateTabsContainers(target = undefined, forAnimation = false) {
|
||||
updateTabsContainers(target = undefined, forAnimation = false) {
|
||||
this.makeSureEmptyTabIsFirst();
|
||||
if (target && !target.target?.parentNode) {
|
||||
target = null;
|
||||
@@ -2414,7 +2439,7 @@ class nsZenWorkspaces {
|
||||
if (target?.type === 'TabClose' || target?.type === 'TabOpen') {
|
||||
animateContainer = target.target.pinned;
|
||||
}
|
||||
await this.onPinnedTabsResize(
|
||||
this.onPinnedTabsResize(
|
||||
// This is what happens when we join a resize observer, an event listener
|
||||
// while using it as a method.
|
||||
[{ target: (target?.target ? target.target : target) ?? this.pinnedTabsContainer }],
|
||||
@@ -2462,7 +2487,7 @@ class nsZenWorkspaces {
|
||||
}
|
||||
}
|
||||
|
||||
async onPinnedTabsResize(entries, forAnimation = false, animateContainer = false) {
|
||||
onPinnedTabsResize(entries, forAnimation = false, animateContainer = false) {
|
||||
if (
|
||||
document.documentElement.hasAttribute('inDOMFullscreen') ||
|
||||
!this._hasInitializedTabsStrip ||
|
||||
@@ -2486,9 +2511,7 @@ class nsZenWorkspaces {
|
||||
// Get all workspaces that have the same userContextId
|
||||
const activeWorkspace = this.getActiveWorkspace();
|
||||
const userContextId = activeWorkspace.containerTabId;
|
||||
const workspaces = this._workspaceCache.filter(
|
||||
(w) => w.containerTabId === userContextId && w.uuid !== originalWorkspaceId
|
||||
);
|
||||
const workspaces = this.getWorkspaces().filter((w) => w.containerTabId === userContextId);
|
||||
workspacesIds.push(...workspaces.map((w) => w.uuid));
|
||||
} else {
|
||||
workspacesIds.push(originalWorkspaceId);
|
||||
@@ -2661,7 +2684,7 @@ class nsZenWorkspaces {
|
||||
return tab;
|
||||
}
|
||||
|
||||
async changeWorkspaceShortcut(offset = 1, whileScrolling = false) {
|
||||
async changeWorkspaceShortcut(offset = 1, whileScrolling = false, disableWrap = false) {
|
||||
// Cycle through workspaces
|
||||
let workspaces = this.getWorkspaces();
|
||||
let activeWorkspace = this.getActiveWorkspace();
|
||||
@@ -2669,7 +2692,7 @@ class nsZenWorkspaces {
|
||||
|
||||
// note: offset can be negative
|
||||
let targetIndex = workspaceIndex + offset;
|
||||
if (this.shouldWrapAroundNavigation) {
|
||||
if (this.shouldWrapAroundNavigation && !disableWrap) {
|
||||
// Add length to handle negative indices and loop
|
||||
targetIndex = (targetIndex + workspaces.length) % workspaces.length;
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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/.
|
||||
/* 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/. */
|
||||
|
||||
// Integration of workspace-specific bookmarks into Places
|
||||
window.ZenWorkspaceBookmarksStorage = {
|
||||
|
||||
@@ -155,13 +155,14 @@
|
||||
|
||||
/* Mark workspaces indicator */
|
||||
.zen-current-workspace-indicator {
|
||||
--indicator-gap: 10px;
|
||||
margin-top: 1px;
|
||||
padding: calc(2px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
max-height: var(--zen-workspace-indicator-height);
|
||||
min-height: var(--zen-workspace-indicator-height);
|
||||
gap: 10px;
|
||||
gap: var(--indicator-gap);
|
||||
align-items: center;
|
||||
flex-direction: row !important;
|
||||
max-width: 100%;
|
||||
@@ -368,3 +369,52 @@ zen-workspace {
|
||||
}
|
||||
|
||||
%include create-workspace-form.css
|
||||
|
||||
/* Pinned tabs collapse styles */
|
||||
|
||||
.zen-current-workspace-indicator-chevron {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root[zen-sidebar-expanded] {
|
||||
.zen-current-workspace-indicator-stack {
|
||||
transition: margin-inline-end 0.1s;
|
||||
|
||||
&[no-icon='true'] {
|
||||
margin-inline-end: calc(-1 * (var(--indicator-gap) + 16px));
|
||||
}
|
||||
}
|
||||
|
||||
.zen-current-workspace-indicator-chevron {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transition: transform 0.2s, opacity 0.2s;
|
||||
transform: rotate(90deg);
|
||||
padding: 2px;
|
||||
|
||||
.zen-current-workspace-indicator-stack[no-icon='true'] & {
|
||||
display: flex;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& zen-workspace[haspinnedtabs] .zen-current-workspace-indicator:hover,
|
||||
& zen-workspace[collapsedpinnedtabs] .zen-current-workspace-indicator {
|
||||
.zen-current-workspace-indicator-chevron {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.zen-current-workspace-indicator-stack {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.zen-current-workspace-indicator-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
zen-workspace[collapsedpinnedtabs] .zen-current-workspace-indicator-chevron {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user