mirror of
https://github.com/zen-browser/desktop.git
synced 2025-12-16 19:35:31 +00:00
Compare commits
2 Commits
window-syn
...
drag-and-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04833ad090 | ||
|
|
f044433bd1 |
@@ -40,12 +40,6 @@
|
|||||||
- name: zen.view.window.scheme
|
- name: zen.view.window.scheme
|
||||||
value: 2
|
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
|
- name: zen.view.context-menu.refresh
|
||||||
value: false
|
value: false
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js
|
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..b028c923d24adf0e9dbe12f80deb3ad4fda535eb 100644
|
||||||
--- a/browser/components/tabbrowser/content/drag-and-drop.js
|
--- a/browser/components/tabbrowser/content/drag-and-drop.js
|
||||||
+++ b/browser/components/tabbrowser/content/drag-and-drop.js
|
+++ b/browser/components/tabbrowser/content/drag-and-drop.js
|
||||||
@@ -32,6 +32,9 @@
|
@@ -32,6 +32,9 @@
|
||||||
@@ -12,18 +12,17 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
if (isTab(element)) {
|
if (isTab(element)) {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
@@ -112,6 +115,10 @@
|
@@ -112,6 +115,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||||
+ if (draggedTab && dropEffect === "move") {
|
+ if (draggedTab && dropEffect === "move") {
|
||||||
+ gZenPinnedTabManager.applyDragoverClass(event, draggedTab);
|
|
||||||
+ gZenViewSplitter.onBrowserDragEndToSplit(event);
|
+ gZenViewSplitter.onBrowserDragEndToSplit(event);
|
||||||
+ }
|
+ }
|
||||||
if (
|
if (
|
||||||
(dropEffect == "move" || dropEffect == "copy") &&
|
(dropEffect == "move" || dropEffect == "copy") &&
|
||||||
document == draggedTab.ownerDocument &&
|
document == draggedTab.ownerDocument &&
|
||||||
@@ -266,6 +273,18 @@
|
@@ -266,6 +272,18 @@
|
||||||
|
|
||||||
this._tabDropIndicator.hidden = true;
|
this._tabDropIndicator.hidden = true;
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@@ -42,7 +41,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
if (draggedTab && dropEffect == "copy") {
|
if (draggedTab && dropEffect == "copy") {
|
||||||
let duplicatedDraggedTab;
|
let duplicatedDraggedTab;
|
||||||
let duplicatedTabs = [];
|
let duplicatedTabs = [];
|
||||||
@@ -291,8 +310,9 @@
|
@@ -291,8 +309,9 @@
|
||||||
let translateOffsetY = oldTranslateY % tabHeight;
|
let translateOffsetY = oldTranslateY % tabHeight;
|
||||||
let newTranslateX = oldTranslateX - translateOffsetX;
|
let newTranslateX = oldTranslateX - translateOffsetX;
|
||||||
let newTranslateY = oldTranslateY - translateOffsetY;
|
let newTranslateY = oldTranslateY - translateOffsetY;
|
||||||
@@ -54,7 +53,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
|
|
||||||
if (this._isContainerVerticalPinnedGrid(draggedTab)) {
|
if (this._isContainerVerticalPinnedGrid(draggedTab)) {
|
||||||
// Update both translate axis for pinned vertical expanded tabs
|
// Update both translate axis for pinned vertical expanded tabs
|
||||||
@@ -308,8 +328,8 @@
|
@@ -308,8 +327,8 @@
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let tabs = this._tabbrowserTabs.ariaFocusableItems.slice(
|
let tabs = this._tabbrowserTabs.ariaFocusableItems.slice(
|
||||||
@@ -65,7 +64,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
);
|
);
|
||||||
let size = this._tabbrowserTabs.verticalMode ? "height" : "width";
|
let size = this._tabbrowserTabs.verticalMode ? "height" : "width";
|
||||||
let screenAxis = this._tabbrowserTabs.verticalMode
|
let screenAxis = this._tabbrowserTabs.verticalMode
|
||||||
@@ -362,11 +382,13 @@
|
@@ -362,11 +381,13 @@
|
||||||
this._dragToPinPromoCard,
|
this._dragToPinPromoCard,
|
||||||
];
|
];
|
||||||
let shouldPin =
|
let shouldPin =
|
||||||
@@ -79,7 +78,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
isTab(draggedTab) &&
|
isTab(draggedTab) &&
|
||||||
draggedTab.pinned &&
|
draggedTab.pinned &&
|
||||||
this._tabbrowserTabs.arrowScrollbox.contains(event.target);
|
this._tabbrowserTabs.arrowScrollbox.contains(event.target);
|
||||||
@@ -384,6 +406,7 @@
|
@@ -384,6 +405,7 @@
|
||||||
(oldTranslateY && oldTranslateY != newTranslateY);
|
(oldTranslateY && oldTranslateY != newTranslateY);
|
||||||
} else if (this._tabbrowserTabs.verticalMode) {
|
} else if (this._tabbrowserTabs.verticalMode) {
|
||||||
shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY;
|
shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY;
|
||||||
@@ -87,7 +86,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
} else {
|
} else {
|
||||||
shouldTranslate &&= oldTranslateX && oldTranslateX != newTranslateX;
|
shouldTranslate &&= oldTranslateX && oldTranslateX != newTranslateX;
|
||||||
}
|
}
|
||||||
@@ -440,7 +463,7 @@
|
@@ -440,7 +462,7 @@
|
||||||
item.removeAttribute("tabdrop-samewindow");
|
item.removeAttribute("tabdrop-samewindow");
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
@@ -96,7 +95,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
postTransitionCleanup();
|
postTransitionCleanup();
|
||||||
} else {
|
} else {
|
||||||
let onTransitionEnd = transitionendEvent => {
|
let onTransitionEnd = transitionendEvent => {
|
||||||
@@ -581,6 +604,7 @@
|
@@ -581,6 +603,7 @@
|
||||||
|
|
||||||
let nextItem = this._tabbrowserTabs.ariaFocusableItems[newIndex];
|
let nextItem = this._tabbrowserTabs.ariaFocusableItems[newIndex];
|
||||||
let tabGroup = isTab(nextItem) && nextItem.group;
|
let tabGroup = isTab(nextItem) && nextItem.group;
|
||||||
@@ -104,7 +103,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
gBrowser.loadTabs(urls, {
|
gBrowser.loadTabs(urls, {
|
||||||
inBackground,
|
inBackground,
|
||||||
replace,
|
replace,
|
||||||
@@ -618,7 +642,16 @@
|
@@ -618,7 +641,16 @@
|
||||||
this._expandGroupOnDrop(draggedTab);
|
this._expandGroupOnDrop(draggedTab);
|
||||||
}
|
}
|
||||||
this._resetTabsAfterDrop(draggedTab.ownerDocument);
|
this._resetTabsAfterDrop(draggedTab.ownerDocument);
|
||||||
@@ -122,7 +121,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
if (
|
if (
|
||||||
dt.mozUserCancelled ||
|
dt.mozUserCancelled ||
|
||||||
dt.dropEffect != "none" ||
|
dt.dropEffect != "none" ||
|
||||||
@@ -822,7 +855,10 @@
|
@@ -822,7 +854,10 @@
|
||||||
_getDragTarget(event, { ignoreSides = false } = {}) {
|
_getDragTarget(event, { ignoreSides = false } = {}) {
|
||||||
let { target } = event;
|
let { target } = event;
|
||||||
while (target) {
|
while (target) {
|
||||||
@@ -134,7 +133,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
target = target.parentNode;
|
target = target.parentNode;
|
||||||
@@ -839,14 +875,17 @@
|
@@ -839,14 +874,17 @@
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +153,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
!this._tabbrowserTabs.expandOnHover
|
!this._tabbrowserTabs.expandOnHover
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -877,7 +916,8 @@
|
@@ -877,7 +915,8 @@
|
||||||
isTabGroupLabel(draggedTab) &&
|
isTabGroupLabel(draggedTab) &&
|
||||||
draggedTab._dragData?.expandGroupOnDrop
|
draggedTab._dragData?.expandGroupOnDrop
|
||||||
) {
|
) {
|
||||||
@@ -164,7 +163,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -942,10 +982,7 @@
|
@@ -942,10 +981,7 @@
|
||||||
if (this._isContainerVerticalPinnedGrid(tab)) {
|
if (this._isContainerVerticalPinnedGrid(tab)) {
|
||||||
// In expanded vertical mode, the max number of pinned tabs per row is dynamic
|
// In expanded vertical mode, the max number of pinned tabs per row is dynamic
|
||||||
// Set this before adjusting dragged tab's position
|
// Set this before adjusting dragged tab's position
|
||||||
@@ -176,7 +175,23 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
let tabsPerRow = 0;
|
let tabsPerRow = 0;
|
||||||
let position = RTL_UI
|
let position = RTL_UI
|
||||||
? window.windowUtils.getBoundsWithoutFlushing(
|
? window.windowUtils.getBoundsWithoutFlushing(
|
||||||
@@ -1112,7 +1149,7 @@
|
@@ -1055,7 +1091,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 +1128,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 +1146,7 @@
|
||||||
let dropEffect = this.getDropEffectForTabDrag(event);
|
let dropEffect = this.getDropEffectForTabDrag(event);
|
||||||
let isMovingInTabStrip = !fromTabList && dropEffect == "move";
|
let isMovingInTabStrip = !fromTabList && dropEffect == "move";
|
||||||
let collapseTabGroupDuringDrag =
|
let collapseTabGroupDuringDrag =
|
||||||
@@ -185,7 +200,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
|
|
||||||
tab._dragData = {
|
tab._dragData = {
|
||||||
offsetX: this._tabbrowserTabs.verticalMode
|
offsetX: this._tabbrowserTabs.verticalMode
|
||||||
@@ -1122,7 +1159,7 @@
|
@@ -1122,7 +1156,7 @@
|
||||||
? event.screenY - window.screenY - tabOffset
|
? event.screenY - window.screenY - tabOffset
|
||||||
: event.screenY - window.screenY,
|
: event.screenY - window.screenY,
|
||||||
scrollPos:
|
scrollPos:
|
||||||
@@ -194,7 +209,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition
|
? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition
|
||||||
: this._tabbrowserTabs.arrowScrollbox.scrollPosition,
|
: this._tabbrowserTabs.arrowScrollbox.scrollPosition,
|
||||||
screenX: event.screenX,
|
screenX: event.screenX,
|
||||||
@@ -1149,6 +1186,7 @@
|
@@ -1149,6 +1183,7 @@
|
||||||
|
|
||||||
if (collapseTabGroupDuringDrag) {
|
if (collapseTabGroupDuringDrag) {
|
||||||
tab.group.collapsed = true;
|
tab.group.collapsed = true;
|
||||||
@@ -202,7 +217,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1173,6 +1211,16 @@
|
@@ -1173,6 +1208,16 @@
|
||||||
if (tabStripItemElement.hasAttribute("dragtarget")) {
|
if (tabStripItemElement.hasAttribute("dragtarget")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -219,7 +234,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
let isPinned = tab.pinned;
|
let isPinned = tab.pinned;
|
||||||
let numPinned = gBrowser.pinnedTabCount;
|
let numPinned = gBrowser.pinnedTabCount;
|
||||||
let allTabs = this._tabbrowserTabs.ariaFocusableItems;
|
let allTabs = this._tabbrowserTabs.ariaFocusableItems;
|
||||||
@@ -1624,10 +1672,7 @@
|
@@ -1624,10 +1669,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +246,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
|
|
||||||
let directionX = screenX > dragData.animLastScreenX;
|
let directionX = screenX > dragData.animLastScreenX;
|
||||||
let directionY = screenY > dragData.animLastScreenY;
|
let directionY = screenY > dragData.animLastScreenY;
|
||||||
@@ -1636,6 +1681,8 @@
|
@@ -1636,6 +1678,8 @@
|
||||||
|
|
||||||
let { width: tabWidth, height: tabHeight } =
|
let { width: tabWidth, height: tabHeight } =
|
||||||
draggedTab.getBoundingClientRect();
|
draggedTab.getBoundingClientRect();
|
||||||
@@ -240,7 +255,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
let shiftSizeX = tabWidth * movingTabs.length;
|
let shiftSizeX = tabWidth * movingTabs.length;
|
||||||
let shiftSizeY = tabHeight;
|
let shiftSizeY = tabHeight;
|
||||||
dragData.tabWidth = tabWidth;
|
dragData.tabWidth = tabWidth;
|
||||||
@@ -1672,8 +1719,8 @@
|
@@ -1672,8 +1716,8 @@
|
||||||
let lastBoundX =
|
let lastBoundX =
|
||||||
lastTabInRow.screenX +
|
lastTabInRow.screenX +
|
||||||
lastTabInRow.getBoundingClientRect().width -
|
lastTabInRow.getBoundingClientRect().width -
|
||||||
@@ -251,161 +266,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
translateX = Math.min(Math.max(translateX, firstBoundX), lastBoundX);
|
translateX = Math.min(Math.max(translateX, firstBoundX), lastBoundX);
|
||||||
translateY = Math.min(Math.max(translateY, firstBoundY), lastBoundY);
|
translateY = Math.min(Math.max(translateY, firstBoundY), lastBoundY);
|
||||||
|
|
||||||
@@ -1833,13 +1880,18 @@
|
@@ -2417,6 +2461,7 @@
|
||||||
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() {
|
finishAnimateTabMove() {
|
||||||
@@ -413,13 +274,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
if (!this.#isMovingTab()) {
|
if (!this.#isMovingTab()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -2457,7 +2502,7 @@
|
||||||
this.#setMovingTabMode(false);
|
|
||||||
+ gZenFolders.highlightGroupOnDragOver(null);
|
|
||||||
|
|
||||||
for (let item of this._tabbrowserTabs.ariaFocusableItems) {
|
|
||||||
this._resetGroupTarget(item);
|
|
||||||
@@ -2457,7 +2531,7 @@
|
|
||||||
tab.style.left = "";
|
tab.style.left = "";
|
||||||
tab.style.top = "";
|
tab.style.top = "";
|
||||||
tab.style.maxWidth = "";
|
tab.style.maxWidth = "";
|
||||||
@@ -428,7 +283,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..bc49f4f5a90638d725eca016d00f30d9
|
|||||||
}
|
}
|
||||||
for (let label of draggedTabDocument.getElementsByClassName(
|
for (let label of draggedTabDocument.getElementsByClassName(
|
||||||
"tab-group-label-container"
|
"tab-group-label-container"
|
||||||
@@ -2467,7 +2541,7 @@
|
@@ -2467,7 +2512,7 @@
|
||||||
label.style.left = "";
|
label.style.left = "";
|
||||||
label.style.top = "";
|
label.style.top = "";
|
||||||
label.style.maxWidth = "";
|
label.style.maxWidth = "";
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
||||||
index 6b6c04599fe80983d13d2069ca62b99d8ad70271..a765f2decc3a565226ac8793422474052f476573 100644
|
index 6b6c04599fe80983d13d2069ca62b99d8ad70271..009a9c398e2434b8b6704ed2c75b0f09ecc22ca1 100644
|
||||||
--- a/browser/components/tabbrowser/content/tabs.js
|
--- a/browser/components/tabbrowser/content/tabs.js
|
||||||
+++ b/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);
|
||||||
|
+ : new window.ZenDragAndDrop(this);
|
||||||
|
this.tabDragAndDrop.init();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -436,7 +436,7 @@
|
@@ -436,7 +436,7 @@
|
||||||
// and we're not hitting the scroll buttons.
|
// and we're not hitting the scroll buttons.
|
||||||
if (
|
if (
|
||||||
|
|||||||
547
src/zen/common/ZenDragAndDrop.js
Normal file
547
src/zen/common/ZenDragAndDrop.js
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Wrap in a block to prevent leaking to window scope.
|
||||||
|
{
|
||||||
|
const isTab = (element) => gBrowser.isTab(element);
|
||||||
|
const isTabGroupLabel = (element) => gBrowser.isTabGroupLabel(element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The elements in the tab strip from `this.ariaFocusableItems` that contain
|
||||||
|
* logical information are:
|
||||||
|
*
|
||||||
|
* - <tab> (.tabbrowser-tab)
|
||||||
|
* - <tab-group> label element (.tab-group-label)
|
||||||
|
*
|
||||||
|
* The elements in the tab strip that contain the space inside of the <tabs>
|
||||||
|
* element are:
|
||||||
|
*
|
||||||
|
* - <tab> (.tabbrowser-tab)
|
||||||
|
* - <tab-group> label element wrapper (.tab-group-label-container)
|
||||||
|
*
|
||||||
|
* When working with tab strip items, if you need logical information, you
|
||||||
|
* can get it directly, e.g. `element.elementIndex` or `element._tPos`. If
|
||||||
|
* you need spatial information like position or dimensions, then you should
|
||||||
|
* call this function. For example, `elementToMove(element).getBoundingClientRect()`
|
||||||
|
* or `elementToMove(element).style.top`.
|
||||||
|
*
|
||||||
|
* @param {MozTabbrowserTab|typeof MozTabbrowserTabGroup.labelElement} element
|
||||||
|
* @returns {MozTabbrowserTab|vbox}
|
||||||
|
*/
|
||||||
|
const elementToMove = (element) => {
|
||||||
|
if (element.classList.contains('zen-current-workspace-indicator')) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
if (element.group?.hasAttribute('split-view-group')) {
|
||||||
|
return element.group;
|
||||||
|
}
|
||||||
|
if (isTab(element)) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
if (isTabGroupLabel(element)) {
|
||||||
|
return element.closest('.tab-group-label-container');
|
||||||
|
}
|
||||||
|
throw new Error(`Element "${element.tagName}" is not expected to move`);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.ZenDragAndDrop = class extends window.TabDragAndDrop {
|
||||||
|
#dragOverBackground = null;
|
||||||
|
#lastDropTarget = null;
|
||||||
|
|
||||||
|
constructor(tabbrowserTabs) {
|
||||||
|
super(tabbrowserTabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
startTabDrag(event, tab, ...args) {
|
||||||
|
super.startTabDrag(event, tab, ...args);
|
||||||
|
let dt = event.dataTransfer;
|
||||||
|
|
||||||
|
const { offsetX, offsetY } = this.#getDragImageOffset(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
_animateTabMove(event) {
|
||||||
|
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||||
|
let dragData = draggedTab._dragData;
|
||||||
|
let movingTabs = dragData.movingTabs;
|
||||||
|
let movingTabsSet = dragData.movingTabsSet;
|
||||||
|
|
||||||
|
dragData.animLastScreenPos ??= this._tabbrowserTabs.verticalMode
|
||||||
|
? dragData.screenY
|
||||||
|
: dragData.screenX;
|
||||||
|
let allTabs = this._tabbrowserTabs.ariaFocusableItems;
|
||||||
|
let numEssentials = gBrowser._numZenEssentials;
|
||||||
|
let isEssential = draggedTab.hasAttribute('zen-essential');
|
||||||
|
let tabs = allTabs.slice(
|
||||||
|
isEssential ? 0 : numEssentials,
|
||||||
|
isEssential ? numEssentials : undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
let screen = this._tabbrowserTabs.verticalMode ? event.screenY : event.screenX;
|
||||||
|
if (screen == dragData.animLastScreenPos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let screenForward = screen > dragData.animLastScreenPos;
|
||||||
|
dragData.animLastScreenPos = screen;
|
||||||
|
|
||||||
|
this._clearDragOverGroupingTimer();
|
||||||
|
|
||||||
|
if (this._rtlMode) {
|
||||||
|
tabs.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
let bounds = (ele) => window.windowUtils.getBoundsWithoutFlushing(ele);
|
||||||
|
let logicalForward = screenForward != this._rtlMode;
|
||||||
|
let screenAxis = this._tabbrowserTabs.verticalMode ? 'screenY' : 'screenX';
|
||||||
|
let size = this._tabbrowserTabs.verticalMode ? 'height' : 'width';
|
||||||
|
let { width: tabWidth, height: tabHeight } = bounds(draggedTab);
|
||||||
|
let tabSize = this._tabbrowserTabs.verticalMode ? tabHeight : tabWidth;
|
||||||
|
let translateX = event.screenX - dragData.screenX;
|
||||||
|
let translateY = event.screenY - dragData.screenY;
|
||||||
|
|
||||||
|
dragData.tabWidth = tabWidth;
|
||||||
|
dragData.tabHeight = tabHeight;
|
||||||
|
dragData.translateX = translateX;
|
||||||
|
dragData.translateY = translateY;
|
||||||
|
|
||||||
|
// Move the dragged tab based on the mouse position.
|
||||||
|
let periphery = document.getElementById('tabbrowser-arrowscrollbox-periphery');
|
||||||
|
let lastMovingTab = movingTabs.at(-1);
|
||||||
|
let firstMovingTab = movingTabs[0];
|
||||||
|
let endEdge = (ele) => ele[screenAxis] + bounds(ele)[size];
|
||||||
|
let lastMovingTabScreen = endEdge(lastMovingTab);
|
||||||
|
let firstMovingTabScreen = firstMovingTab[screenAxis];
|
||||||
|
let shiftSize = lastMovingTabScreen - firstMovingTabScreen;
|
||||||
|
let translate = screen - dragData[screenAxis];
|
||||||
|
|
||||||
|
// Constrain the range over which the moving tabs can move between the edge of the tabstrip and periphery.
|
||||||
|
// Add 1 to periphery so we don't overlap it.
|
||||||
|
let startBound = this._rtlMode
|
||||||
|
? endEdge(periphery) + 1 - firstMovingTabScreen
|
||||||
|
: this._tabbrowserTabs[screenAxis] - firstMovingTabScreen;
|
||||||
|
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;
|
||||||
|
translate = Math.min(Math.max(translate, startBound), endBound);
|
||||||
|
|
||||||
|
// Center the tab under the cursor if the tab is not under the cursor while dragging
|
||||||
|
let draggedTabScreenAxis = draggedTab[screenAxis] + translate;
|
||||||
|
if (
|
||||||
|
(screen < draggedTabScreenAxis || screen > draggedTabScreenAxis + tabSize) &&
|
||||||
|
draggedTabScreenAxis + tabSize < endBound &&
|
||||||
|
draggedTabScreenAxis > startBound
|
||||||
|
) {
|
||||||
|
translate = screen - draggedTab[screenAxis] - tabSize / 2;
|
||||||
|
// Ensure, after the above calculation, we are still within bounds
|
||||||
|
translate = Math.min(Math.max(translate, startBound), endBound);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gBrowser.pinnedTabCount && !this._dragToPinPromoCard.shouldRender) {
|
||||||
|
let pinnedDropIndicatorMargin = parseFloat(
|
||||||
|
window.getComputedStyle(this._pinnedDropIndicator).marginInline
|
||||||
|
);
|
||||||
|
this._checkWithinPinnedContainerBounds({
|
||||||
|
firstMovingTabScreen,
|
||||||
|
lastMovingTabScreen,
|
||||||
|
pinnedTabsStartEdge: this._rtlMode
|
||||||
|
? endEdge(this._tabbrowserTabs.arrowScrollbox) + pinnedDropIndicatorMargin
|
||||||
|
: this[screenAxis],
|
||||||
|
pinnedTabsEndEdge: this._rtlMode
|
||||||
|
? endEdge(this._tabbrowserTabs)
|
||||||
|
: this._tabbrowserTabs.arrowScrollbox[screenAxis] - pinnedDropIndicatorMargin,
|
||||||
|
translate,
|
||||||
|
draggedTab,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dragData.translatePos = translate;
|
||||||
|
|
||||||
|
tabs = tabs.filter((t) => !movingTabsSet.has(t) || t == draggedTab);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the `draggedTab` is just starting to move, the `draggedTab` is in
|
||||||
|
* its original location and the `dropElementIndex == draggedTab.elementIndex`.
|
||||||
|
* Any tabs or tab group labels passed in as `item` will result in a 0 shift
|
||||||
|
* because all of those items should also continue to appear in their original
|
||||||
|
* locations.
|
||||||
|
*
|
||||||
|
* Once the `draggedTab` is more "backward" in the tab strip than its original
|
||||||
|
* position, any tabs or tab group labels between the `draggedTab`'s original
|
||||||
|
* `elementIndex` and the current `dropElementIndex` should shift "forward"
|
||||||
|
* out of the way of the dragging tabs.
|
||||||
|
*
|
||||||
|
* When the `draggedTab` is more "forward" in the tab strip than its original
|
||||||
|
* position, any tabs or tab group labels between the `draggedTab`'s original
|
||||||
|
* `elementIndex` and the current `dropElementIndex` should shift "backward"
|
||||||
|
* out of the way of the dragging tabs.
|
||||||
|
*
|
||||||
|
* @param {MozTabbrowserTab|MozTabbrowserTabGroup.label} item
|
||||||
|
* @param {number} dropElementIndex
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
let getTabShift = (item, dropElementIndex) => {
|
||||||
|
if (item.elementIndex < draggedTab.elementIndex && item.elementIndex >= dropElementIndex) {
|
||||||
|
return this._rtlMode ? -shiftSize : shiftSize;
|
||||||
|
}
|
||||||
|
if (item.elementIndex > draggedTab.elementIndex && item.elementIndex < dropElementIndex) {
|
||||||
|
return this._rtlMode ? shiftSize : -shiftSize;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
let oldDropElementIndex = dragData.animDropElementIndex ?? movingTabs[0].elementIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the higher % by which one element overlaps another
|
||||||
|
* in the tab strip.
|
||||||
|
*
|
||||||
|
* When element 1 is further forward in the tab strip:
|
||||||
|
*
|
||||||
|
* p1 p2 p1+s1 p2+s2
|
||||||
|
* | | | |
|
||||||
|
* ---------------------------------
|
||||||
|
* ========================
|
||||||
|
* s1
|
||||||
|
* ===================
|
||||||
|
* s2
|
||||||
|
* ==========
|
||||||
|
* overlap
|
||||||
|
*
|
||||||
|
* When element 2 is further forward in the tab strip:
|
||||||
|
*
|
||||||
|
* p2 p1 p2+s2 p1+s1
|
||||||
|
* | | | |
|
||||||
|
* ---------------------------------
|
||||||
|
* ========================
|
||||||
|
* s2
|
||||||
|
* ===================
|
||||||
|
* s1
|
||||||
|
* ==========
|
||||||
|
* overlap
|
||||||
|
*
|
||||||
|
* @param {number} p1
|
||||||
|
* Position (x or y value in screen coordinates) of element 1.
|
||||||
|
* @param {number} s1
|
||||||
|
* Size (width or height) of element 1.
|
||||||
|
* @param {number} p2
|
||||||
|
* Position (x or y value in screen coordinates) of element 2.
|
||||||
|
* @param {number} s2
|
||||||
|
* Size (width or height) of element 1.
|
||||||
|
* @returns {number}
|
||||||
|
* Percent between 0.0 and 1.0 (inclusive) of element 1 or element 2
|
||||||
|
* that is overlapped by the other element. If the elements have
|
||||||
|
* different sizes, then this returns the larger overlap percentage.
|
||||||
|
*/
|
||||||
|
function greatestOverlap(p1, s1, p2, s2) {
|
||||||
|
let overlapSize;
|
||||||
|
if (p1 < p2) {
|
||||||
|
// element 1 starts first
|
||||||
|
overlapSize = p1 + s1 - p2;
|
||||||
|
} else {
|
||||||
|
// element 2 starts first
|
||||||
|
overlapSize = p2 + s2 - p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No overlap if size is <= 0
|
||||||
|
if (overlapSize <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the overlap fraction from each element's perspective.
|
||||||
|
let overlapPercent = Math.max(overlapSize / s1, overlapSize / s2);
|
||||||
|
|
||||||
|
return Math.min(overlapPercent, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine what tab/tab group label we're dragging over.
|
||||||
|
*
|
||||||
|
* When dragging right or downwards, the reference point for overlap is
|
||||||
|
* the right or bottom edge of the most forward moving tab.
|
||||||
|
*
|
||||||
|
* When dragging left or upwards, the reference point for overlap is the
|
||||||
|
* left or top edge of the most backward moving tab.
|
||||||
|
*
|
||||||
|
* @returns {Element|null}
|
||||||
|
* The tab or tab group label that should be used to visually shift tab
|
||||||
|
* strip elements out of the way of the dragged tab(s) during a drag
|
||||||
|
* operation. Note: this is not used to determine where the dragged
|
||||||
|
* tab(s) will be dropped, it is only used for visual animation at this
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
let getOverlappedElement = () => {
|
||||||
|
let point = (screenForward ? lastMovingTabScreen : firstMovingTabScreen) + translate;
|
||||||
|
let low = 0;
|
||||||
|
let high = tabs.length - 1;
|
||||||
|
while (low <= high) {
|
||||||
|
let mid = Math.floor((low + high) / 2);
|
||||||
|
if (tabs[mid] == draggedTab && ++mid > high) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let element = tabs[mid];
|
||||||
|
let elementForSize = elementToMove(element);
|
||||||
|
screen = elementForSize[screenAxis] + getTabShift(element, oldDropElementIndex);
|
||||||
|
|
||||||
|
if (screen > point) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else if (screen + bounds(elementForSize)[size] < point) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
let dropElement = getOverlappedElement();
|
||||||
|
|
||||||
|
let newDropElementIndex;
|
||||||
|
if (dropElement) {
|
||||||
|
newDropElementIndex = dropElement.elementIndex;
|
||||||
|
} else {
|
||||||
|
// When the dragged element(s) moves past a tab strip item, the dragged
|
||||||
|
// element's leading edge starts dragging over empty space, resulting in
|
||||||
|
// no overlapping `dropElement`. In these cases, try to fall back to the
|
||||||
|
// previous animation drop element index to avoid unstable animations
|
||||||
|
// (tab strip items snapping back and forth to shift out of the way of
|
||||||
|
// the dragged element(s)).
|
||||||
|
newDropElementIndex = oldDropElementIndex;
|
||||||
|
|
||||||
|
// We always want to have a `dropElement` so that we can determine where to
|
||||||
|
// logically drop the dragged element(s).
|
||||||
|
//
|
||||||
|
// It's tempting to set `dropElement` to
|
||||||
|
// `this.ariaFocusableItems.at(oldDropElementIndex)`, and that is correct
|
||||||
|
// for most cases, but there are edge cases:
|
||||||
|
//
|
||||||
|
// 1) the drop element index range needs to be one larger than the number of
|
||||||
|
// items that can move in the tab strip. The simplest example is when all
|
||||||
|
// tabs are ungrouped and unpinned: for 5 tabs, the drop element index needs
|
||||||
|
// to be able to go from 0 (become the first tab) to 5 (become the last tab).
|
||||||
|
// `this.ariaFocusableItems.at(5)` would be `undefined` when dragging to the
|
||||||
|
// end of the tab strip. In this specific case, it works to fall back to
|
||||||
|
// setting the drop element to the last tab.
|
||||||
|
//
|
||||||
|
// 2) the `elementIndex` values of the tab strip items do not change during
|
||||||
|
// the drag operation. When dragging the last tab or multiple tabs at the end
|
||||||
|
// of the tab strip, having `dropElement` fall back to the last tab makes the
|
||||||
|
// drop element one of the moving tabs. This can have some unexpected behavior
|
||||||
|
// if not careful. Falling back to the last tab that's not moving (instead of
|
||||||
|
// just the last tab) helps ensure that `dropElement` is always a stable target
|
||||||
|
// to drop next to.
|
||||||
|
//
|
||||||
|
// 3) all of the elements in the tab strip are moving, in which case there can't
|
||||||
|
// be a drop element and it should stay `undefined`.
|
||||||
|
//
|
||||||
|
// 4) we just started dragging and the `oldDropElementIndex` has its default
|
||||||
|
// valuë of `movingTabs[0].elementIndex`. In this case, the drop element
|
||||||
|
// shouldn't be a moving tab, so keep it `undefined`.
|
||||||
|
let lastPossibleDropElement = this._rtlMode
|
||||||
|
? tabs.find((t) => t != draggedTab)
|
||||||
|
: tabs.findLast((t) => t != draggedTab);
|
||||||
|
let maxElementIndexForDropElement = lastPossibleDropElement?.elementIndex;
|
||||||
|
if (Number.isInteger(maxElementIndexForDropElement)) {
|
||||||
|
let index = Math.min(oldDropElementIndex, maxElementIndexForDropElement);
|
||||||
|
let oldDropElementCandidate = this._tabbrowserTabs.ariaFocusableItems.at(index);
|
||||||
|
if (!movingTabsSet.has(oldDropElementCandidate)) {
|
||||||
|
dropElement = oldDropElementCandidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let moveOverThreshold;
|
||||||
|
let overlapPercent;
|
||||||
|
let dropBefore;
|
||||||
|
if (dropElement) {
|
||||||
|
let dropElementForOverlap = elementToMove(dropElement);
|
||||||
|
|
||||||
|
let dropElementScreen = dropElementForOverlap[screenAxis];
|
||||||
|
let dropElementPos = dropElementScreen + getTabShift(dropElement, oldDropElementIndex);
|
||||||
|
let dropElementSize = bounds(dropElementForOverlap)[size];
|
||||||
|
let firstMovingTabPos = firstMovingTabScreen + translate;
|
||||||
|
overlapPercent = greatestOverlap(
|
||||||
|
firstMovingTabPos,
|
||||||
|
shiftSize,
|
||||||
|
dropElementPos,
|
||||||
|
dropElementSize
|
||||||
|
);
|
||||||
|
|
||||||
|
moveOverThreshold = gBrowser._tabGroupsEnabled
|
||||||
|
? Services.prefs.getIntPref('browser.tabs.dragDrop.moveOverThresholdPercent') / 100
|
||||||
|
: 0.5;
|
||||||
|
moveOverThreshold = Math.min(1, Math.max(0, moveOverThreshold));
|
||||||
|
let shouldMoveOver = overlapPercent > moveOverThreshold;
|
||||||
|
if (logicalForward && shouldMoveOver) {
|
||||||
|
newDropElementIndex++;
|
||||||
|
} else if (!logicalForward && !shouldMoveOver) {
|
||||||
|
newDropElementIndex++;
|
||||||
|
if (newDropElementIndex > oldDropElementIndex) {
|
||||||
|
// FIXME: Not quite sure what's going on here, but this check
|
||||||
|
// prevents jittery back-and-forth movement of background tabs
|
||||||
|
// in certain cases.
|
||||||
|
newDropElementIndex = oldDropElementIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate the overlap with the updated drop index for when the
|
||||||
|
// drop element moves over.
|
||||||
|
dropElementPos = dropElementScreen + getTabShift(dropElement, newDropElementIndex);
|
||||||
|
overlapPercent = greatestOverlap(
|
||||||
|
firstMovingTabPos,
|
||||||
|
shiftSize,
|
||||||
|
dropElementPos,
|
||||||
|
dropElementSize
|
||||||
|
);
|
||||||
|
dropBefore = firstMovingTabPos < dropElementPos;
|
||||||
|
if (this._rtlMode) {
|
||||||
|
dropBefore = !dropBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tabbrowserTabs.removeAttribute('movingtab-group');
|
||||||
|
this._resetGroupTarget(document.querySelector('[dragover-groupTarget]'));
|
||||||
|
|
||||||
|
delete dragData.shouldDropIntoCollapsedTabGroup;
|
||||||
|
|
||||||
|
// Default to dropping into `dropElement`'s tab group, if it exists.
|
||||||
|
let dropElementGroup = dropElement?.group;
|
||||||
|
let colorCode = dropElementGroup?.color;
|
||||||
|
|
||||||
|
let lastUnmovingTabInGroup = dropElementGroup?.tabs.findLast((t) => !movingTabsSet.has(t));
|
||||||
|
if (
|
||||||
|
isTab(dropElement) &&
|
||||||
|
dropElementGroup &&
|
||||||
|
dropElement == lastUnmovingTabInGroup &&
|
||||||
|
!dropBefore
|
||||||
|
) {
|
||||||
|
// Dragging tab over the last tab of a tab group, but not enough
|
||||||
|
// for it to drop into the tab group. Drop it after the tab group instead.
|
||||||
|
dropElement = dropElementGroup;
|
||||||
|
colorCode = undefined;
|
||||||
|
} else if (isTabGroupLabel(dropElement)) {
|
||||||
|
// Dropping right before the first tab in the tab group.
|
||||||
|
dropElement = dropElementGroup.tabs[0];
|
||||||
|
dropBefore = true;
|
||||||
|
}
|
||||||
|
this._setDragOverGroupColor(colorCode);
|
||||||
|
this._tabbrowserTabs.toggleAttribute('movingtab-addToGroup', colorCode);
|
||||||
|
this._tabbrowserTabs.toggleAttribute('movingtab-ungroup', !colorCode);
|
||||||
|
|
||||||
|
this.#applyDragoverIndicator(event, tabs, movingTabs, overlapPercent);
|
||||||
|
|
||||||
|
if (
|
||||||
|
newDropElementIndex == oldDropElementIndex &&
|
||||||
|
dropBefore == dragData.dropBefore &&
|
||||||
|
dropElement == dragData.dropElement
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragData.dropElement = dropElement;
|
||||||
|
dragData.dropBefore = dropBefore;
|
||||||
|
dragData.animDropElementIndex = newDropElementIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_dragend(event) {
|
||||||
|
super.handle_dragend(event);
|
||||||
|
this.#removeDragOverBackground();
|
||||||
|
gZenPinnedTabManager.removeTabContainersDragoverClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
#applyDragOverBackground(element) {
|
||||||
|
if (this.#dragOverBackground && this.#lastDropTarget === element) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const margin = 2;
|
||||||
|
const rect = window.windowUtils.getBoundsWithoutFlushing(element);
|
||||||
|
this.#dragOverBackground = document.createElement('div');
|
||||||
|
this.#dragOverBackground.id = 'zen-dragover-background';
|
||||||
|
this.#dragOverBackground.style.height = `${rect.height - margin * 2}px`;
|
||||||
|
this.#dragOverBackground.style.top = `${rect.top + margin}px`;
|
||||||
|
gNavToolbox.appendChild(this.#dragOverBackground);
|
||||||
|
this.#lastDropTarget = element;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#removeDragOverBackground() {
|
||||||
|
if (this.#dragOverBackground) {
|
||||||
|
this.#dragOverBackground.remove();
|
||||||
|
this.#dragOverBackground = null;
|
||||||
|
this.#lastDropTarget = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#applyDragoverIndicator(event, tabs, movingTabs, overlapPercent) {
|
||||||
|
const separation = 4;
|
||||||
|
const dropZoneSelector = ':is(.tabbrowser-tab, .zen-drop-target, .tab-group-label)';
|
||||||
|
let shouldPlayHapticFeedback = false;
|
||||||
|
let dropElement = event.target.closest(dropZoneSelector);
|
||||||
|
if (!dropElement) {
|
||||||
|
const numEssentials = gBrowser._numZenEssentials;
|
||||||
|
const numPinned = gBrowser.pinnedTabCount - numEssentials;
|
||||||
|
const tabToUse = event.target.closest(dropZoneSelector);
|
||||||
|
if (!tabToUse) {
|
||||||
|
this.#removeDragOverBackground();
|
||||||
|
gZenPinnedTabManager.removeTabContainersDragoverClass();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isPinned = tabToUse.pinned;
|
||||||
|
const relativeTabs = tabs.slice(isPinned ? 0 : numPinned, isPinned ? numPinned : undefined);
|
||||||
|
const draggedTabRect = elementToMove(tabToUse).getBoundingClientRect();
|
||||||
|
dropElement = event.clientY > draggedTabRect.top ? relativeTabs.at(-1) : relativeTabs[0];
|
||||||
|
}
|
||||||
|
dropElement = elementToMove(dropElement);
|
||||||
|
if (this.#lastDropTarget !== dropElement) {
|
||||||
|
shouldPlayHapticFeedback = this.#lastDropTarget !== null;
|
||||||
|
this.#removeDragOverBackground();
|
||||||
|
}
|
||||||
|
let canHightlightGroup =
|
||||||
|
gZenFolders.highlightGroupOnDragOver(dropElement.parentElement, movingTabs) ||
|
||||||
|
!dropElement.parentElement?.isZenFolder;
|
||||||
|
if (isTab(dropElement)) {
|
||||||
|
const indicator = gZenPinnedTabManager.dragIndicator;
|
||||||
|
let rect = dropElement.getBoundingClientRect();
|
||||||
|
let top = 0;
|
||||||
|
const threshold =
|
||||||
|
Services.prefs.getIntPref('browser.tabs.dragDrop.moveOverThresholdPercent') / 100;
|
||||||
|
if (overlapPercent > threshold) {
|
||||||
|
top = Math.round(rect.top + rect.height) + 'px';
|
||||||
|
} else {
|
||||||
|
top = Math.round(rect.top) + 'px';
|
||||||
|
}
|
||||||
|
if (indicator.style.top !== top) {
|
||||||
|
shouldPlayHapticFeedback = true;
|
||||||
|
}
|
||||||
|
indicator.setAttribute('orientation', 'horizontal');
|
||||||
|
indicator.style.setProperty('--indicator-left', rect.left + separation / 2 + 'px');
|
||||||
|
indicator.style.setProperty('--indicator-width', rect.width - separation + 'px');
|
||||||
|
indicator.style.top = top;
|
||||||
|
indicator.style.removeProperty('left');
|
||||||
|
} else if (dropElement.classList.contains('zen-drop-target') && canHightlightGroup) {
|
||||||
|
// removeTabContainersDragoverClass Already calls a new haptic feedback
|
||||||
|
shouldPlayHapticFeedback =
|
||||||
|
this.#applyDragOverBackground(dropElement) && !gZenPinnedTabManager._dragIndicator;
|
||||||
|
gZenPinnedTabManager.removeTabContainersDragoverClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldPlayHapticFeedback) {
|
||||||
|
Services.zen.playHapticFeedback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#getDragImageOffset(tab) {
|
||||||
|
const { offsetX, offsetY } = tab._dragData;
|
||||||
|
const rect = tab.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
offsetX: offsetX - rect.left,
|
||||||
|
offsetY: offsetY - rect.top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,4 +11,6 @@
|
|||||||
ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenMods.mjs", { global: "current" });
|
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/ZenKeyboardShortcuts.mjs", { global: "current" });
|
||||||
ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenSessionStore.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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/modules/ZenSessionStore.mjs)
|
content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/modules/ZenSessionStore.mjs)
|
||||||
content/browser/zen-components/ZenHasPolyfill.mjs (../../zen/common/modules/ZenHasPolyfill.mjs)
|
content/browser/zen-components/ZenHasPolyfill.mjs (../../zen/common/modules/ZenHasPolyfill.mjs)
|
||||||
content/browser/zen-components/ZenSidebarNotification.mjs (../../zen/common/modules/ZenSidebarNotification.mjs)
|
content/browser/zen-components/ZenSidebarNotification.mjs (../../zen/common/modules/ZenSidebarNotification.mjs)
|
||||||
|
content/browser/zen-components/ZenDragAndDrop.js (../../zen/common/ZenDragAndDrop.js)
|
||||||
|
|
||||||
content/browser/zen-components/ZenEmojisData.min.mjs (../../zen/common/emojis/ZenEmojisData.min.mjs)
|
content/browser/zen-components/ZenEmojisData.min.mjs (../../zen/common/emojis/ZenEmojisData.min.mjs)
|
||||||
content/browser/zen-components/ZenEmojiPicker.mjs (../../zen/common/emojis/ZenEmojiPicker.mjs)
|
content/browser/zen-components/ZenEmojiPicker.mjs (../../zen/common/emojis/ZenEmojiPicker.mjs)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class ZenFolder extends MozTabbrowserTabGroup {
|
|||||||
#initialized = false;
|
#initialized = false;
|
||||||
|
|
||||||
static markup = `
|
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"/>
|
<html:div class="tab-group-folder-icon"/>
|
||||||
<label class="tab-group-label" role="button"/>
|
<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"/>
|
<image class="tab-reset-button reset-icon" role="button" keyNav="false" data-l10n-id="zen-folders-unload-all-tooltip"/>
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ function formatRelativeTime(timestamp) {
|
|||||||
|
|
||||||
class nsZenFolders extends nsZenDOMOperatedFeature {
|
class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||||
#ZEN_MAX_SUBFOLDERS = Services.prefs.getIntPref('zen.folders.max-subfolders', 5);
|
#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;
|
#popup = null;
|
||||||
#popupTimer = null;
|
#popupTimer = null;
|
||||||
@@ -100,7 +98,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
|||||||
.getElementById('context_zenChangeFolderSpace')
|
.getElementById('context_zenChangeFolderSpace')
|
||||||
.querySelector('menupopup');
|
.querySelector('menupopup');
|
||||||
changeFolderSpace.innerHTML = '';
|
changeFolderSpace.innerHTML = '';
|
||||||
for (const workspace of [...gZenWorkspaces._workspaceCache.workspaces].reverse()) {
|
for (const workspace of [...gZenWorkspaces.getWorkspaces()].reverse()) {
|
||||||
const item = gZenWorkspaces.generateMenuItemForWorkspace(workspace);
|
const item = gZenWorkspaces.generateMenuItemForWorkspace(workspace);
|
||||||
item.addEventListener('command', (event) => {
|
item.addEventListener('command', (event) => {
|
||||||
if (!this.#lastFolderContextMenu) return;
|
if (!this.#lastFolderContextMenu) return;
|
||||||
@@ -1065,18 +1063,16 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
|||||||
* @param {Array<MozTabbrowserTab>|null} movingTabs The tabs being moved.
|
* @param {Array<MozTabbrowserTab>|null} movingTabs The tabs being moved.
|
||||||
*/
|
*/
|
||||||
highlightGroupOnDragOver(folder, movingTabs) {
|
highlightGroupOnDragOver(folder, movingTabs) {
|
||||||
if (folder === this.#lastHighlightedGroup) return;
|
if (folder === this.#lastHighlightedGroup) return true;
|
||||||
const tab = movingTabs ? movingTabs[0] : null;
|
const tab = movingTabs ? movingTabs[0] : null;
|
||||||
if (this.#lastHighlightedGroup && this.#lastHighlightedGroup !== folder) {
|
if (this.#lastHighlightedGroup && this.#lastHighlightedGroup !== folder) {
|
||||||
this.#lastHighlightedGroup.removeAttribute('selected');
|
|
||||||
if (this.#lastHighlightedGroup.collapsed) {
|
if (this.#lastHighlightedGroup.collapsed) {
|
||||||
this.updateFolderIcon(this.#lastHighlightedGroup, 'close');
|
this.updateFolderIcon(this.#lastHighlightedGroup, 'close');
|
||||||
}
|
}
|
||||||
this.#lastHighlightedGroup = null;
|
this.#lastHighlightedGroup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
folder &&
|
folder?.isZenFolder &&
|
||||||
(!folder.hasAttribute('split-view-group') || !folder.hasAttribute('selected')) &&
|
(!folder.hasAttribute('split-view-group') || !folder.hasAttribute('selected')) &&
|
||||||
folder !== tab?.group &&
|
folder !== tab?.group &&
|
||||||
!(
|
!(
|
||||||
@@ -1084,13 +1080,13 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
|||||||
movingTabs?.some((t) => gBrowser.isTabGroupLabel(t))
|
movingTabs?.some((t) => gBrowser.isTabGroupLabel(t))
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
folder.setAttribute('selected', 'true');
|
|
||||||
folder.style.transform = '';
|
|
||||||
if (folder.collapsed) {
|
if (folder.collapsed) {
|
||||||
this.updateFolderIcon(folder, 'open');
|
this.updateFolderIcon(folder, 'open');
|
||||||
}
|
}
|
||||||
this.#lastHighlightedGroup = folder;
|
this.#lastHighlightedGroup = folder;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1103,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) {
|
#normalizeGroupItems(items) {
|
||||||
return items
|
return items
|
||||||
.filter((item) => !item.hasAttribute('zen-empty-tab'))
|
.filter((item) => !item.hasAttribute('zen-empty-tab'))
|
||||||
|
|||||||
@@ -503,15 +503,16 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
moveToAnotherTabContainerIfNecessary(event, movingTabs) {
|
moveToAnotherTabContainerIfNecessary(event, movingTabs) {
|
||||||
|
movingTabs = [...movingTabs];
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
movingTabs = [...movingTabs];
|
|
||||||
try {
|
try {
|
||||||
const pinnedTabsTarget =
|
const pinnedTabsTarget = event.target.closest(
|
||||||
event.target.closest('.zen-current-workspace-indicator') || this._isGoingToPinnedTabs;
|
':is(.zen-current-workspace-indicator, .zen-workspace-pinned-tabs-section)'
|
||||||
|
);
|
||||||
const essentialTabsTarget = event.target.closest('.zen-essentials-container');
|
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
|
// TODO: Solve the issue of adding a tab between two groups
|
||||||
// Remove group labels from the moving tabs and replace it
|
// Remove group labels from the moving tabs and replace it
|
||||||
@@ -703,56 +704,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
|||||||
: [separator];
|
: [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() {
|
get dragIndicator() {
|
||||||
if (!this._dragIndicator) {
|
if (!this._dragIndicator) {
|
||||||
this._dragIndicator = document.createElement('div');
|
this._dragIndicator = document.createElement('div');
|
||||||
|
|||||||
@@ -1281,9 +1281,9 @@
|
|||||||
width: calc(var(--indicator-width) - 2 * var(--zen-drag-indicator-height) - 4px);
|
width: calc(var(--indicator-width) - 2 * var(--zen-drag-indicator-height) - 4px);
|
||||||
height: var(--zen-drag-indicator-height);
|
height: var(--zen-drag-indicator-height);
|
||||||
transition:
|
transition:
|
||||||
top 0.1s ease-out,
|
top 0.05s ease-out,
|
||||||
left 0.1s ease-out,
|
left 0.05s ease-out,
|
||||||
width 0.1s ease-out;
|
width 0.05s ease-out;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
left: calc(-2 * var(--zen-drag-indicator-height));
|
left: calc(-2 * var(--zen-drag-indicator-height));
|
||||||
@@ -1370,3 +1370,13 @@
|
|||||||
.tab-group-label-container[zen-dragtarget] {
|
.tab-group-label-container[zen-dragtarget] {
|
||||||
z-index: 9 !important;
|
z-index: 9 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
class nsZenWorkspace extends MozXULElement {
|
class nsZenWorkspace extends MozXULElement {
|
||||||
static get markup() {
|
static get markup() {
|
||||||
return `
|
return `
|
||||||
<vbox class="zen-workspace-tabs-section zen-current-workspace-indicator" flex="1" context="zenWorkspaceMoreActions">
|
<vbox class="zen-workspace-tabs-section zen-current-workspace-indicator zen-drop-target" flex="1" context="zenWorkspaceMoreActions">
|
||||||
<hbox class="zen-current-workspace-indicator-icon" />
|
<hbox class="zen-current-workspace-indicator-icon" />
|
||||||
<label class="zen-current-workspace-indicator-name" flex="1" />
|
<label class="zen-current-workspace-indicator-name" flex="1" />
|
||||||
<toolbarbutton class="toolbarbutton-1 chromeclass-toolbar-additional zen-workspaces-actions" context="zenWorkspaceMoreActions" />
|
<toolbarbutton class="toolbarbutton-1 chromeclass-toolbar-additional zen-workspaces-actions" context="zenWorkspaceMoreActions" />
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ class nsZenWorkspaces {
|
|||||||
direction: null,
|
direction: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
_workspaceCache = {};
|
|
||||||
|
|
||||||
#lastScrollTime = 0;
|
#lastScrollTime = 0;
|
||||||
|
|
||||||
bookmarkMenus = [
|
bookmarkMenus = [
|
||||||
|
|||||||
Reference in New Issue
Block a user