From c6c2a2fe5af3d749a0268eb890b6ce150a91c3ff Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Wed, 21 Jan 2026 13:59:52 +0100 Subject: [PATCH] fix: Fixed drag and drop not using the actual pointer location, b=no-bug, c=folders, tabs, workspaces --- src/zen/drag-and-drop/ZenDragAndDrop.js | 58 ++++++++++++------------- src/zen/folders/ZenFolders.mjs | 4 +- src/zen/folders/zen-folders.css | 4 +- src/zen/tabs/ZenPinnedTabManager.mjs | 2 +- src/zen/tabs/zen-tabs/vertical-tabs.css | 6 +-- src/zen/workspaces/zen-workspaces.css | 2 +- 6 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/zen/drag-and-drop/ZenDragAndDrop.js b/src/zen/drag-and-drop/ZenDragAndDrop.js index 8441f0bef..57999c9ed 100644 --- a/src/zen/drag-and-drop/ZenDragAndDrop.js +++ b/src/zen/drag-and-drop/ZenDragAndDrop.js @@ -39,6 +39,7 @@ !element || element.closest(".zen-current-workspace-indicator") || element.hasAttribute("split-view-group") || + element.classList.contains("zen-drop-target") || isEssentialsPromo(element) ) { return element; @@ -52,6 +53,9 @@ if (isTabGroupLabel(element)) { return element.closest(".tab-group-label-container"); } + if (gBrowser.isTabGroup(element)) { + return element.labelContainerElement; + } throw new Error(`Element "${element.tagName}" is not expected to move`); }; @@ -111,7 +115,7 @@ const { offsetX, offsetY } = this.#getDragImageOffset(event, tab, draggingTabs); const dragImage = this.#createDragImageForTabs(draggingTabs); this.originalDragImageArgs = [dragImage, offsetX, offsetY]; - dt.updateDragImage(...this.originalDragImageArgs); + dt.setDragImage(...this.originalDragImageArgs); if (tab.hasAttribute("zen-essential")) { setTimeout(() => { tab.style.visibility = "hidden"; @@ -137,6 +141,9 @@ const rect = tab.getBoundingClientRect(); tabClone.style.minWidth = tabClone.style.maxWidth = `${rect.width}px`; tabClone.style.minHeight = tabClone.style.maxHeight = `${rect.height}px`; + if (tabClone.hasAttribute("visuallyselected")) { + tabClone.style.transform = "translate(-50%, -50%)"; + } } if (i > 0) { tabClone.style.transform = `translate(${i * 4}px, -${i * (tabRect.height - 4)}px)`; @@ -521,7 +528,7 @@ [dropBefore, dropElement] = this.#applyDragoverIndicator( event, - tabs, + dropElement, movingTabs, draggedTab ) ?? [dropBefore, dropElement]; @@ -646,7 +653,7 @@ clientX < 0 || clientX > innerWidth || clientY < 0 || clientY > innerHeight; if (isOutOfWindow && !this.#isOutOfWindow) { this.#isOutOfWindow = true; - this.#maybeClearVerticalPinnedGridDragOver(); + this.maybeClearVerticalPinnedGridDragOver(); this.clearSpaceSwitchTimer(); this.clearDragOverVisuals(); const dt = event.dataTransfer; @@ -678,8 +685,8 @@ handle_drop(event) { this.clearSpaceSwitchTimer(); + gZenFolders.highlightGroupOnDragOver(null); super.handle_drop(event); - this.#maybeClearVerticalPinnedGridDragOver(); const dt = event.dataTransfer; const activeWorkspace = gZenWorkspaces.activeWorkspace; let draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); @@ -817,11 +824,14 @@ if (currentEssenialContainer?.essentialsPromo) { currentEssenialContainer.essentialsPromo.remove(); } + // We also call it here to ensure we clear any highlight if the drop happened + // outside of a valid drop target. + gZenFolders.highlightGroupOnDragOver(null); this.ZenDragAndDropService.onDragEnd(); super.handle_dragend(event); this.#removeDragOverBackground(); gZenPinnedTabManager.removeTabContainersDragoverClass(); - this.#maybeClearVerticalPinnedGridDragOver(); + this.maybeClearVerticalPinnedGridDragOver(); this.originalDragImageArgs = []; window.removeEventListener("dragover", this.handle_windowDragEnter, { capture: true }); this.#isOutOfWindow = false; @@ -872,53 +882,43 @@ } // eslint-disable-next-line complexity - #applyDragoverIndicator(event, tabs, movingTabs, draggedTab) { + #applyDragoverIndicator(event, dropElement, movingTabs, draggedTab) { const separation = 4; const dropZoneSelector = - ":is(.tabbrowser-tab, .zen-drop-target, .tab-group-label, tab-group[split-view-group])"; + ":is(.tabbrowser-tab, .zen-drop-target, .tab-group-label-container, tab-group[split-view-group])"; let shouldPlayHapticFeedback = false; let showIndicatorUnderNewTabButton = false; let dropBefore = false; - let dropElement = event.target.closest(dropZoneSelector); - if (!dropElement) { + let dropElementFromEvent = event.target.closest(dropZoneSelector); + if (!dropElementFromEvent) { if (event.target.classList.contains("zen-workspace-empty-space")) { dropElement = this._tabbrowserTabs.ariaFocusableItems.at(-1); // Only if there are no normal tabs to drop after showIndicatorUnderNewTabButton = gBrowser.tabs[gBrowser.tabs.length - 1].hasAttribute("zen-empty-tab"); - } else { - const numEssentials = gBrowser._numZenEssentials; - const numPinned = gBrowser.pinnedTabCount - numEssentials; - const tabToUse = - event.target.closest(dropZoneSelector) || draggedTab._dragData?.dropElement; - if (!tabToUse) { - return null; - } - 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); - this.#maybeClearVerticalPinnedGridDragOver(); + this.maybeClearVerticalPinnedGridDragOver(); if (this.#lastDropTarget !== dropElement) { shouldPlayHapticFeedback = this.#lastDropTarget !== null; this.#removeDragOverBackground(); } - let isZenFolder = dropElement.parentElement?.isZenFolder; + let possibleFolderElement = dropElement.parentElement; + let isZenFolder = possibleFolderElement?.isZenFolder; let canHightlightGroup = - gZenFolders.highlightGroupOnDragOver(dropElement.parentElement, movingTabs) || !isZenFolder; + gZenFolders.highlightGroupOnDragOver(possibleFolderElement, movingTabs) || !isZenFolder; let rect = window.windowUtils.getBoundsWithoutFlushing(dropElement); const overlapPercent = (event.clientY - rect.top) / rect.height; // We wan't to leave a small threshold (20% for example) so we can drag tabs below and above // a folder label without dragging into the folder. let threshold = Services.prefs.getIntPref("zen.tabs.folder-dragover-threshold-percent") / 100; let dropIntoFolder = - isZenFolder && (overlapPercent < threshold || overlapPercent > 1 - threshold); + isZenFolder && + (overlapPercent < threshold || + (overlapPercent > 1 - threshold && + (possibleFolderElement.collapsed || + possibleFolderElement.childGroupsAndTabs.length < 2))); if ( isTabGroupLabel(draggedTab) && draggedTab.group?.isZenFolder && @@ -1234,7 +1234,7 @@ } } - #maybeClearVerticalPinnedGridDragOver() { + maybeClearVerticalPinnedGridDragOver() { if (this._fakeEssentialTab) { this._fakeEssentialTab.remove(); delete this._fakeEssentialTab; diff --git a/src/zen/folders/ZenFolders.mjs b/src/zen/folders/ZenFolders.mjs index a33b89a5f..3cdd78027 100644 --- a/src/zen/folders/ZenFolders.mjs +++ b/src/zen/folders/ZenFolders.mjs @@ -1134,11 +1134,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature { * @param {MozTabbrowserTabGroup|undefined|null} folder The folder to highlight, or null to clear highlight. * @param {Array|null} movingTabs The tabs being moved. */ - highlightGroupOnDragOver(folder, movingTabs) { + highlightGroupOnDragOver(folder, movingTabs = null) { if (folder === this.#lastHighlightedGroup) { return true; } - const tab = movingTabs ? movingTabs[0] : null; if (this.#lastHighlightedGroup && this.#lastHighlightedGroup !== folder) { if (this.#lastHighlightedGroup.collapsed) { this.updateFolderIcon(this.#lastHighlightedGroup, "close"); @@ -1148,7 +1147,6 @@ class nsZenFolders extends nsZenDOMOperatedFeature { if ( folder?.isZenFolder && (!folder.hasAttribute("split-view-group") || !folder.hasAttribute("selected")) && - folder !== tab?.group && !( folder.level >= this.#ZEN_MAX_SUBFOLDERS && movingTabs?.some((t) => gBrowser.isTabGroupLabel(t)) diff --git a/src/zen/folders/zen-folders.css b/src/zen/folders/zen-folders.css index 5c21c8b02..8ee863983 100644 --- a/src/zen/folders/zen-folders.css +++ b/src/zen/folders/zen-folders.css @@ -123,11 +123,11 @@ zen-folder { } &[state="open"] .back { - transform: skewX(16deg) translate(-2px, 3.4px) scale(0.85); + transform: skewX(16deg) translate(-4px, 2px) scale(0.85); } &[state="open"] :is(.front, .dots, .icon) { - transform: skewX(-16deg) translate(11.1px, 3.4px) scale(0.85); + transform: skewX(-16deg) translate(8px, 2px) scale(0.85); } & .icon { diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index b1ff7bf07..a0a944255 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -560,6 +560,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { ); const essentialTabsTarget = event.target.closest(".zen-essentials-container"); const tabsTarget = !pinnedTabsTarget; + gBrowser.tabContainer.tabDragAndDrop.maybeClearVerticalPinnedGridDragOver(); // TODO: Solve the issue of adding a tab between two groups // Remove group labels from the moving tabs and replace it @@ -651,7 +652,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } } } - return moved; } catch (ex) { console.error("Error moving tabs:", ex); diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index b6916e9ca..2dd3e1ddb 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -497,7 +497,7 @@ } #tabbrowser-tabs { - --toolbarbutton-inner-padding: 6px !important; + --toolbarbutton-inner-padding: 10px !important; --tab-icon-end-margin: var(--toolbarbutton-inner-padding) !important; } @@ -670,7 +670,7 @@ .tab-sharing-icon-overlay, .tab-icon-overlay { margin-inline-end: var(--toolbarbutton-inner-padding) !important; - margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 4) !important; + margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 3) !important; } } } @@ -934,7 +934,7 @@ height: calc(100% - var(--tab-block-margin) * 2); margin-left: calc(-1 * var(--tab-inline-padding) + var(--tab-block-margin)); margin-right: 8px; - padding: 0 calc(var(--toolbarbutton-inner-padding) - 2px) 0 calc(var(--toolbarbutton-inner-padding) / 4 + var(--tab-inline-padding) - 2px); + padding: 0 calc(var(--toolbarbutton-inner-padding) - 2px) 0 calc(var(--toolbarbutton-inner-padding) / 3 + var(--tab-inline-padding) - 2px); border-radius: 0; border-top-left-radius: var(--border-radius-medium); width: unset; diff --git a/src/zen/workspaces/zen-workspaces.css b/src/zen/workspaces/zen-workspaces.css index f0f05d22b..1c2750872 100644 --- a/src/zen/workspaces/zen-workspaces.css +++ b/src/zen/workspaces/zen-workspaces.css @@ -156,7 +156,7 @@ /* Mark workspaces indicator */ .zen-current-workspace-indicator { --indicator-gap: var(--toolbarbutton-inner-padding); - padding: calc(2px + var(--tab-inline-padding) + var(--zen-toolbox-padding)); + padding: calc(3px + var(--tab-inline-padding) + var(--zen-toolbox-padding)); font-weight: 500; position: relative; max-height: var(--zen-workspace-indicator-height);