From 1e1236838b8e7b44831d2644a2764264ea723826 Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Wed, 24 Dec 2025 16:13:16 +0100 Subject: [PATCH] feat: Started working on drag and dropping to essentials, b=no-bug, c=no-component --- .../tabbrowser/content/drag-and-drop-js.patch | 58 +++++++++++++++++-- src/zen/drag-and-drop/ZenDragAndDrop.js | 56 +++++++++++++++--- 2 files changed, 100 insertions(+), 14 deletions(-) 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 ca3788222..d04c7ac74 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..78c8f0ad686e72d281370c934d2dbf5f70311117 100644 +index 97b931c3c7385a52d20204369fcf6d6999053687..6c24d636459d1d83b6d8dcf8157b3f25b0158436 100644 --- a/browser/components/tabbrowser/content/drag-and-drop.js +++ b/browser/components/tabbrowser/content/drag-and-drop.js @@ -32,6 +32,9 @@ @@ -222,7 +222,18 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..78c8f0ad686e72d281370c934d2dbf5f let isPinned = tab.pinned; let numPinned = gBrowser.pinnedTabCount; let allTabs = this._tabbrowserTabs.ariaFocusableItems; -@@ -1624,10 +1657,7 @@ +@@ -1608,8 +1641,9 @@ + + _animateExpandedPinnedTabMove(event) { + let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0); ++ let zenFakeTab = this._invisibleTempTab || draggedTab; + let dragData = draggedTab._dragData; +- let movingTabs = dragData.movingTabs; ++ let movingTabs = this._invisibleTempTab ? [this._invisibleTempTab] : dragData.movingTabs; + + dragData.animLastScreenX ??= dragData.screenX; + dragData.animLastScreenY ??= dragData.screenY; +@@ -1624,10 +1658,7 @@ return; } @@ -234,16 +245,18 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..78c8f0ad686e72d281370c934d2dbf5f let directionX = screenX > dragData.animLastScreenX; let directionY = screenY > dragData.animLastScreenY; -@@ -1636,6 +1666,8 @@ +@@ -1635,7 +1666,9 @@ + dragData.animLastScreenX = screenX; let { width: tabWidth, height: tabHeight } = - draggedTab.getBoundingClientRect(); +- draggedTab.getBoundingClientRect(); ++ zenFakeTab.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 +1704,8 @@ +@@ -1672,8 +1705,8 @@ let lastBoundX = lastTabInRow.screenX + lastTabInRow.getBoundingClientRect().width - @@ -254,6 +267,41 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..78c8f0ad686e72d281370c934d2dbf5f translateX = Math.min(Math.max(translateX, firstBoundX), lastBoundX); translateY = Math.min(Math.max(translateY, firstBoundY), lastBoundY); +@@ -1704,7 +1737,6 @@ + // * We're doing a binary search in order to reduce the amount of + // tabs we need to check. + +- tabs = tabs.filter(t => !movingTabs.includes(t) || t == draggedTab); + let firstTabCenterX = firstMovingTabScreenX + translateX + tabWidth / 2; + let lastTabCenterX = lastMovingTabScreenX + translateX + tabWidth / 2; + let tabCenterX = directionX ? lastTabCenterX : firstTabCenterX; +@@ -1716,7 +1748,7 @@ + + let getTabShift = (tab, dropIndex) => { + if ( +- tab.elementIndex < draggedTab.elementIndex && ++ tab.elementIndex < zenFakeTab.elementIndex && + tab.elementIndex >= dropIndex + ) { + // If tab is at the end of a row, shift back and down +@@ -1733,7 +1765,7 @@ + return [RTL_UI ? -shiftSizeX : shiftSizeX, 0]; + } + if ( +- tab.elementIndex > draggedTab.elementIndex && ++ tab.elementIndex > zenFakeTab.elementIndex && + tab.elementIndex < dropIndex + ) { + // If tab is not index 0 and at the start of a row, shift across and up +@@ -1759,7 +1791,7 @@ + dragData.animDropElementIndex ?? movingTabs[0].elementIndex; + while (low <= high) { + let mid = Math.floor((low + high) / 2); +- if (tabs[mid] == draggedTab && ++mid > high) { ++ if (tabs[mid] == zenFakeTab && ++mid > high) { + break; + } + let [shiftX, shiftY] = getTabShift(tabs[mid], oldIndex); @@ -2457,7 +2489,7 @@ tab.style.left = ""; tab.style.top = ""; diff --git a/src/zen/drag-and-drop/ZenDragAndDrop.js b/src/zen/drag-and-drop/ZenDragAndDrop.js index 0ff95bf55..a8470cc90 100644 --- a/src/zen/drag-and-drop/ZenDragAndDrop.js +++ b/src/zen/drag-and-drop/ZenDragAndDrop.js @@ -428,10 +428,12 @@ delete dragData.shouldDropIntoCollapsedTabGroup; - [dropBefore, dropElement] = this.#applyDragoverIndicator(event, tabs, movingTabs) ?? [ - dropBefore, - dropElement, - ]; + [dropBefore, dropElement] = this.#applyDragoverIndicator( + event, + tabs, + movingTabs, + draggedTab + ) ?? [dropBefore, dropElement]; // Default to dropping into `dropElement`'s tab group, if it exists. let dropElementGroup = dropElement?.group; @@ -501,7 +503,8 @@ } handle_windowDragLeave(event) { - if (!this.#isMovingTab() || !this._tabbrowserTabs._dndCanvas) { + const canvas = this._tabbrowserTabs._dndCanvas; + if (!this.#isMovingTab() || !canvas) { return; } let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0); @@ -516,8 +519,18 @@ this.#isOutOfWindow = true; this.clearDragOverVisuals(); const dt = event.dataTransfer; + if (!this._browserDragImageWrapper) { + const wrappingDiv = document.createXULElement('vbox'); + wrappingDiv.style.borderRadius = canvas.style.borderRadius = '8px'; + wrappingDiv.style.border = '2px solid white'; + wrappingDiv.style.width = 200 + 'px'; + wrappingDiv.style.height = 130 + 'px'; + wrappingDiv.appendChild(canvas); + this._browserDragImageWrapper = wrappingDiv; + document.documentElement.appendChild(wrappingDiv); + } dt.updateDragImage( - this._tabbrowserTabs._dndCanvas, + this._browserDragImageWrapper, this.originalDragImageArgs[1], this.originalDragImageArgs[2] ); @@ -604,6 +617,11 @@ this.originalDragImageArgs = []; window.removeEventListener('dragover', this.handle_windowDragEnter, { capture: true }); this.#isOutOfWindow = false; + this.#clearInvisibleTempTab(); + if (this._browserDragImageWrapper) { + this._browserDragImageWrapper.remove(); + delete this._browserDragImageWrapper; + } } #applyDragOverBackground(element) { @@ -634,7 +652,15 @@ gZenPinnedTabManager.removeTabContainersDragoverClass(); } - #applyDragoverIndicator(event, tabs, movingTabs) { + #clearInvisibleTempTab() { + if (this._invisibleTempTab) { + this._invisibleTempTab.remove(); + delete this._invisibleTempTab; + this._tabbrowserTabs._invalidateCachedTabs(); + } + } + + #applyDragoverIndicator(event, tabs, movingTabs, draggedTab) { const separation = 4; const dropZoneSelector = ':is(.tabbrowser-tab, .zen-drop-target, .tab-group-label)'; let shouldPlayHapticFeedback = false; @@ -654,9 +680,20 @@ dropElement = event.clientY > draggedTabRect.top ? relativeTabs.at(-1) : relativeTabs[0]; } dropElement = elementToMove(dropElement); - if (this._isContainerVerticalPinnedGrid(dropElement)) { + if (this._isContainerVerticalPinnedGrid(dropElement) && isTab(draggedTab)) { + if (!draggedTab.hasAttribute('zen-essential') && !this._invisibleTempTab) { + this._invisibleTempTab = draggedTab.cloneNode(true); + this._invisibleTempTab.setAttribute('zen-essential', 'true'); + //this._invisibleTempTab.style.visibility = 'hidden'; + this._tabbrowserTabs.ariaFocusableItems[gBrowser._numZenEssentials - 1].after( + this._invisibleTempTab + ); + this._tabbrowserTabs._invalidateCachedTabs(); + } this._animateExpandedPinnedTabMove(event); return; + } else if (this._invisibleTempTab) { + this.#clearInvisibleTempTab(); } if (this.#lastDropTarget !== dropElement) { shouldPlayHapticFeedback = this.#lastDropTarget !== null; @@ -673,7 +710,8 @@ let dropIntoFolder = isZenFolder && (overlapPercent < threshold || overlapPercent > 1 - threshold); if ( - movingTabs[0].group?.isZenFolder && + isTabGroupLabel(draggedTab) && + draggedTab.group?.isZenFolder && isTab(dropElement) && (!dropElement.pinned || dropElement.hasAttribute('zen-essential')) ) {