diff --git a/src/browser/components/tabbrowser/content/drag-and-drop-js.patch b/src/browser/components/tabbrowser/content/drag-and-drop-js.patch index 7ef70d218..ca3788222 100644 --- a/src/browser/components/tabbrowser/content/drag-and-drop-js.patch +++ b/src/browser/components/tabbrowser/content/drag-and-drop-js.patch @@ -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..4fa28e4c00b746b34e63b3ece15b79edfecd9117 100644 +index 97b931c3c7385a52d20204369fcf6d6999053687..78c8f0ad686e72d281370c934d2dbf5f70311117 100644 --- a/browser/components/tabbrowser/content/drag-and-drop.js +++ b/browser/components/tabbrowser/content/drag-and-drop.js @@ -32,6 +32,9 @@ @@ -22,15 +22,12 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed if ( (dropEffect == "move" || dropEffect == "copy") && document == draggedTab.ownerDocument && -@@ -266,6 +272,18 @@ +@@ -266,6 +272,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) { @@ -41,7 +38,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed if (draggedTab && dropEffect == "copy") { let duplicatedDraggedTab; let duplicatedTabs = []; -@@ -291,8 +309,9 @@ +@@ -291,8 +306,9 @@ let translateOffsetY = oldTranslateY % tabHeight; let newTranslateX = oldTranslateX - translateOffsetX; let newTranslateY = oldTranslateY - translateOffsetY; @@ -53,7 +50,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed if (this._isContainerVerticalPinnedGrid(draggedTab)) { // Update both translate axis for pinned vertical expanded tabs -@@ -308,8 +327,8 @@ +@@ -308,8 +324,8 @@ } } else { let tabs = this._tabbrowserTabs.ariaFocusableItems.slice( @@ -64,7 +61,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed ); let size = this._tabbrowserTabs.verticalMode ? "height" : "width"; let screenAxis = this._tabbrowserTabs.verticalMode -@@ -362,11 +381,13 @@ +@@ -362,11 +378,13 @@ this._dragToPinPromoCard, ]; let shouldPin = @@ -78,7 +75,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed isTab(draggedTab) && draggedTab.pinned && this._tabbrowserTabs.arrowScrollbox.contains(event.target); -@@ -384,6 +405,7 @@ +@@ -384,6 +402,7 @@ (oldTranslateY && oldTranslateY != newTranslateY); } else if (this._tabbrowserTabs.verticalMode) { shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY; @@ -86,7 +83,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed } else { shouldTranslate &&= oldTranslateX && oldTranslateX != newTranslateX; } -@@ -440,7 +462,7 @@ +@@ -440,7 +459,7 @@ item.removeAttribute("tabdrop-samewindow"); resolve(); }; @@ -95,7 +92,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed postTransitionCleanup(); } else { let onTransitionEnd = transitionendEvent => { -@@ -581,6 +603,7 @@ +@@ -581,6 +600,7 @@ let nextItem = this._tabbrowserTabs.ariaFocusableItems[newIndex]; let tabGroup = isTab(nextItem) && nextItem.group; @@ -103,7 +100,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed gBrowser.loadTabs(urls, { inBackground, replace, -@@ -618,7 +641,16 @@ +@@ -618,7 +638,16 @@ this._expandGroupOnDrop(draggedTab); } this._resetTabsAfterDrop(draggedTab.ownerDocument); @@ -121,7 +118,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed if ( dt.mozUserCancelled || dt.dropEffect != "none" || -@@ -822,7 +854,10 @@ +@@ -822,7 +851,10 @@ _getDragTarget(event, { ignoreSides = false } = {}) { let { target } = event; while (target) { @@ -133,7 +130,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed break; } target = target.parentNode; -@@ -839,14 +874,17 @@ +@@ -839,14 +871,17 @@ return null; } } @@ -153,7 +150,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed !this._tabbrowserTabs.expandOnHover ); } -@@ -877,7 +915,8 @@ +@@ -877,7 +912,8 @@ isTabGroupLabel(draggedTab) && draggedTab._dragData?.expandGroupOnDrop ) { @@ -163,7 +160,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed } } -@@ -942,10 +981,7 @@ +@@ -942,10 +978,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 @@ -175,7 +172,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed let tabsPerRow = 0; let position = RTL_UI ? window.windowUtils.getBoundsWithoutFlushing( -@@ -1055,7 +1091,6 @@ +@@ -1055,7 +1088,6 @@ // using updateDragImage. On Linux, we can use a panel. if (platform == "win" || platform == "macosx") { captureListener = function () { @@ -183,7 +180,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed }; } else { // Create a panel to use it in setDragImage -@@ -1093,7 +1128,6 @@ +@@ -1093,7 +1125,6 @@ ); dragImageOffset = dragImageOffset * scale; } @@ -191,7 +188,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed // _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 @@ +@@ -1112,7 +1143,7 @@ let dropEffect = this.getDropEffectForTabDrag(event); let isMovingInTabStrip = !fromTabList && dropEffect == "move"; let collapseTabGroupDuringDrag = @@ -200,7 +197,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed tab._dragData = { offsetX: this._tabbrowserTabs.verticalMode -@@ -1122,7 +1156,7 @@ +@@ -1122,7 +1153,7 @@ ? event.screenY - window.screenY - tabOffset : event.screenY - window.screenY, scrollPos: @@ -209,7 +206,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed ? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition : this._tabbrowserTabs.arrowScrollbox.scrollPosition, screenX: event.screenX, -@@ -1149,6 +1183,7 @@ +@@ -1149,6 +1180,7 @@ if (collapseTabGroupDuringDrag) { tab.group.collapsed = true; @@ -217,7 +214,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed } } } -@@ -1173,6 +1208,7 @@ +@@ -1173,6 +1205,7 @@ if (tabStripItemElement.hasAttribute("dragtarget")) { return; } @@ -225,7 +222,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed let isPinned = tab.pinned; let numPinned = gBrowser.pinnedTabCount; let allTabs = this._tabbrowserTabs.ariaFocusableItems; -@@ -1624,10 +1660,7 @@ +@@ -1624,10 +1657,7 @@ return; } @@ -237,7 +234,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed let directionX = screenX > dragData.animLastScreenX; let directionY = screenY > dragData.animLastScreenY; -@@ -1636,6 +1669,8 @@ +@@ -1636,6 +1666,8 @@ let { width: tabWidth, height: tabHeight } = draggedTab.getBoundingClientRect(); @@ -246,7 +243,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed let shiftSizeX = tabWidth * movingTabs.length; let shiftSizeY = tabHeight; dragData.tabWidth = tabWidth; -@@ -1672,8 +1707,8 @@ +@@ -1672,8 +1704,8 @@ let lastBoundX = lastTabInRow.screenX + lastTabInRow.getBoundingClientRect().width - @@ -257,15 +254,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed translateX = Math.min(Math.max(translateX, firstBoundX), lastBoundX); translateY = Math.min(Math.max(translateY, firstBoundY), lastBoundY); -@@ -2417,6 +2452,7 @@ - } - - finishAnimateTabMove() { -+ gZenPinnedTabManager.onDragFinish(); - if (!this.#isMovingTab()) { - return; - } -@@ -2457,7 +2493,7 @@ +@@ -2457,7 +2489,7 @@ tab.style.left = ""; tab.style.top = ""; tab.style.maxWidth = ""; @@ -274,7 +263,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79ed } for (let label of draggedTabDocument.getElementsByClassName( "tab-group-label-container" -@@ -2467,7 +2503,7 @@ +@@ -2467,7 +2499,7 @@ label.style.left = ""; label.style.top = ""; label.style.maxWidth = ""; diff --git a/src/zen/drag-and-drop/ZenDragAndDrop.js b/src/zen/drag-and-drop/ZenDragAndDrop.js index a19d7f7f0..0ff95bf55 100644 --- a/src/zen/drag-and-drop/ZenDragAndDrop.js +++ b/src/zen/drag-and-drop/ZenDragAndDrop.js @@ -474,6 +474,16 @@ return this._tabbrowserTabs.hasAttribute('movingtab'); } + 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]; + } + handle_dragover(event) { super.handle_dragover(event); if (event.target.closest('#tabbrowser-tabbox')) { @@ -518,6 +528,74 @@ } } + handle_drop_transition(dropElement, draggedTab, movingTabs, dropBefore) { + if ( + gReduceMotion || + dropElement.group !== draggedTab.group || + dropElement.hasAttribute('zen-essential') || + draggedTab.hasAttribute('zen-essential') + ) { + return; + } + const animateElement = (ele, translateY) => { + ele.style.transform = `translateY(${translateY}px)`; + setTimeout(() => { + setTimeout(() => { + gZenUIManager.motion + .animate( + ele, + { + y: [translateY, 0], + }, + { + duration: 0.1, + bounce: 0, + } + ) + .then(() => { + ele.style.transform = ''; + }); + }); + }); + }; + const items = this._tabbrowserTabs.ariaFocusableItems; + let rect = window.windowUtils.getBoundsWithoutFlushing(draggedTab); + let tabsInBetween = []; + let startIndex = Math.min(draggedTab.elementIndex, dropElement.elementIndex + !dropBefore); + let endIndex = Math.max(draggedTab.elementIndex, dropElement.elementIndex - dropBefore); + for (let i = startIndex; i <= endIndex; i++) { + let tab = items[i]; + if (!movingTabs.includes(tab) && isTab(tab)) { + tabsInBetween.push(tab); + } + } + let extraTranslate = 0; + let translateY = + draggedTab.elementIndex > dropElement.elementIndex ? -rect.height : rect.height; + translateY *= movingTabs.length; + if (draggedTab.pinned != dropElement.pinned) { + const shiftableItems = this.#dragShiftableItems; + for (let item of shiftableItems) { + // We also need to animate these shiftable items and add it to the extraTranslate + // so the dragged tab ends up in the right position. + let itemRect = window.windowUtils.getBoundsWithoutFlushing(item); + extraTranslate += itemRect.height; + animateElement(item, translateY); + } + } + // Animate tabs in between moving out of the way + for (let tab of tabsInBetween) { + animateElement(tab, translateY); + } + let draggedTabTranslateY = + draggedTab.elementIndex > dropElement.elementIndex + ? rect.height * tabsInBetween.length + : -rect.height * tabsInBetween.length; + draggedTabTranslateY += + extraTranslate * (draggedTab.elementIndex > dropElement.elementIndex ? 1 : -1); + animateElement(draggedTab, draggedTabTranslateY); + } + handle_dragend(event) { this.ZenDragAndDropService.onDragEnd(); super.handle_dragend(event); diff --git a/src/zen/split-view/ZenViewSplitter.mjs b/src/zen/split-view/ZenViewSplitter.mjs index 42b048ad0..63ffe346a 100644 --- a/src/zen/split-view/ZenViewSplitter.mjs +++ b/src/zen/split-view/ZenViewSplitter.mjs @@ -437,7 +437,6 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { return; } const side = this.fakeBrowser.getAttribute('side'); - if (this._draggingTab) this._draggingTab.setAttribute('zen-has-splitted', 'true'); this._lastOpenedTab = gBrowser.selectedTab; this._draggingTab = null; event.dataTransfer.updateDragImage( diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index d1a06d935..78b7be434 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -662,45 +662,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]; - } - get dragIndicator() { if (!this._dragIndicator) { this._dragIndicator = document.createElement('div');