From 0573aef7422d6777a832c631434e7e80887b746a Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Tue, 23 Dec 2025 16:42:10 +0100 Subject: [PATCH] feat: Implement opacity changes to the drag image, b=no-bug, c=tabs, common, split-view --- prefs/zen/zen.yaml | 3 + .../base/content/zen-assets.jar.inc.mn | 1 + .../tabbrowser/content/drag-and-drop-js.patch | 25 ++-- .../tabbrowser/content/tabs-js.patch | 4 +- src/widget/cocoa/nsDragService-mm.patch | 47 +++++++ src/zen/common/jar.inc.mn | 1 - .../ZenDragAndDrop.js | 33 ++++- src/zen/drag-and-drop/components.conf | 14 ++ src/zen/drag-and-drop/jar.inc.mn | 5 + src/zen/drag-and-drop/moz.build | 31 +++++ src/zen/drag-and-drop/nsIZenDragAndDrop.idl | 25 ++++ src/zen/drag-and-drop/nsZenDragAndDrop.cpp | 48 +++++++ src/zen/drag-and-drop/nsZenDragAndDrop.h | 45 +++++++ src/zen/moz.build | 1 + src/zen/split-view/ZenViewSplitter.mjs | 122 +++++++++++------- src/zen/split-view/zen-decks.css | 41 +++--- src/zen/tabs/zen-tabs/vertical-tabs.css | 5 - 17 files changed, 348 insertions(+), 103 deletions(-) create mode 100644 src/widget/cocoa/nsDragService-mm.patch rename src/zen/{common => drag-and-drop}/ZenDragAndDrop.js (96%) create mode 100644 src/zen/drag-and-drop/components.conf create mode 100644 src/zen/drag-and-drop/jar.inc.mn create mode 100644 src/zen/drag-and-drop/moz.build create mode 100644 src/zen/drag-and-drop/nsIZenDragAndDrop.idl create mode 100644 src/zen/drag-and-drop/nsZenDragAndDrop.cpp create mode 100644 src/zen/drag-and-drop/nsZenDragAndDrop.h diff --git a/prefs/zen/zen.yaml b/prefs/zen/zen.yaml index e5be93492..9cb6734be 100644 --- a/prefs/zen/zen.yaml +++ b/prefs/zen/zen.yaml @@ -20,6 +20,9 @@ - name: zen.tabs.close-window-with-empty value: true +- name: zen.tabs.use-legacy-drag-and-drop + value: false + - name: zen.tabs.folder-dragover-threshold-percent value: 20 # Percentage of folder height to trigger dragover diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn index 04f9cfb52..d97cdfaf6 100644 --- a/src/browser/base/content/zen-assets.jar.inc.mn +++ b/src/browser/base/content/zen-assets.jar.inc.mn @@ -4,6 +4,7 @@ #include ../../../zen/common/jar.inc.mn #include ../../../zen/compact-mode/jar.inc.mn +#include ../../../zen/drag-and-drop/jar.inc.mn #include ../../../zen/split-view/jar.inc.mn #include ../../../zen/mods/jar.inc.mn #include ../../../zen/workspaces/jar.inc.mn 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 d15be39c7..7ef70d218 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..b028c923d24adf0e9dbe12f80deb3ad4fda535eb 100644 +index 97b931c3c7385a52d20204369fcf6d6999053687..4fa28e4c00b746b34e63b3ece15b79edfecd9117 100644 --- a/browser/components/tabbrowser/content/drag-and-drop.js +++ b/browser/components/tabbrowser/content/drag-and-drop.js @@ -32,6 +32,9 @@ @@ -217,24 +217,15 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..b028c923d24adf0e9dbe12f80deb3ad4 } } } -@@ -1173,6 +1208,16 @@ +@@ -1173,6 +1208,7 @@ if (tabStripItemElement.hasAttribute("dragtarget")) { return; } -+ let { movingTabs: zenMovingTabs } = tab._dragData; -+ for (let movingTab of zenMovingTabs.slice(zenMovingTabs.findIndex(t => t._tPos == tab._tPos))) { -+ if (isTabGroupLabel(tab)) { -+ movingTab = movingTab.parentElement; -+ } -+ // "dragtarget" contains the following rules which must only be set AFTER the above -+ // elements have been adjusted. {z-index: 3 !important, position: absolute !important} -+ movingTab.setAttribute("zen-dragtarget", ""); -+ } + return; let isPinned = tab.pinned; let numPinned = gBrowser.pinnedTabCount; let allTabs = this._tabbrowserTabs.ariaFocusableItems; -@@ -1624,10 +1669,7 @@ +@@ -1624,10 +1660,7 @@ return; } @@ -246,7 +237,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..b028c923d24adf0e9dbe12f80deb3ad4 let directionX = screenX > dragData.animLastScreenX; let directionY = screenY > dragData.animLastScreenY; -@@ -1636,6 +1678,8 @@ +@@ -1636,6 +1669,8 @@ let { width: tabWidth, height: tabHeight } = draggedTab.getBoundingClientRect(); @@ -255,7 +246,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..b028c923d24adf0e9dbe12f80deb3ad4 let shiftSizeX = tabWidth * movingTabs.length; let shiftSizeY = tabHeight; dragData.tabWidth = tabWidth; -@@ -1672,8 +1716,8 @@ +@@ -1672,8 +1707,8 @@ let lastBoundX = lastTabInRow.screenX + lastTabInRow.getBoundingClientRect().width - @@ -266,7 +257,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..b028c923d24adf0e9dbe12f80deb3ad4 translateX = Math.min(Math.max(translateX, firstBoundX), lastBoundX); translateY = Math.min(Math.max(translateY, firstBoundY), lastBoundY); -@@ -2417,6 +2461,7 @@ +@@ -2417,6 +2452,7 @@ } finishAnimateTabMove() { @@ -274,7 +265,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..b028c923d24adf0e9dbe12f80deb3ad4 if (!this.#isMovingTab()) { return; } -@@ -2457,7 +2502,7 @@ +@@ -2457,7 +2493,7 @@ tab.style.left = ""; tab.style.top = ""; tab.style.maxWidth = ""; @@ -283,7 +274,7 @@ index 97b931c3c7385a52d20204369fcf6d6999053687..b028c923d24adf0e9dbe12f80deb3ad4 } for (let label of draggedTabDocument.getElementsByClassName( "tab-group-label-container" -@@ -2467,7 +2512,7 @@ +@@ -2467,7 +2503,7 @@ label.style.left = ""; label.style.top = ""; label.style.maxWidth = ""; diff --git a/src/browser/components/tabbrowser/content/tabs-js.patch b/src/browser/components/tabbrowser/content/tabs-js.patch index 3b660b333..603339ecb 100644 --- a/src/browser/components/tabbrowser/content/tabs-js.patch +++ b/src/browser/components/tabbrowser/content/tabs-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js -index 6b6c04599fe80983d13d2069ca62b99d8ad70271..009a9c398e2434b8b6704ed2c75b0f09ecc22ca1 100644 +index 6b6c04599fe80983d13d2069ca62b99d8ad70271..6d5ae983446bc778f3075d79f8ff14748dd7756f 100644 --- a/browser/components/tabbrowser/content/tabs.js +++ b/browser/components/tabbrowser/content/tabs.js @@ -235,7 +235,7 @@ @@ -7,7 +7,7 @@ index 6b6c04599fe80983d13d2069ca62b99d8ad70271..009a9c398e2434b8b6704ed2c75b0f09 ) ? new window.TabStacking(this) - : new window.TabDragAndDrop(this); -+ : new window.ZenDragAndDrop(this); ++ : Services.prefs.getBoolPref("zen.tabs.use-legacy-drag-and-drop") ? new window.TabDragAndDrop(this) : new window.ZenDragAndDrop(this); this.tabDragAndDrop.init(); } diff --git a/src/widget/cocoa/nsDragService-mm.patch b/src/widget/cocoa/nsDragService-mm.patch new file mode 100644 index 000000000..48afb3c5c --- /dev/null +++ b/src/widget/cocoa/nsDragService-mm.patch @@ -0,0 +1,47 @@ +diff --git a/widget/cocoa/nsDragService.mm b/widget/cocoa/nsDragService.mm +index f1614b823a859ff8fbc74982f205bb1f2ef29beb..897c24846a97c132babe3ad79da12ebfcec90484 100644 +--- a/widget/cocoa/nsDragService.mm ++++ b/widget/cocoa/nsDragService.mm +@@ -23,6 +23,7 @@ + #include "mozilla/PresShell.h" + #include "mozilla/dom/Document.h" + #include "mozilla/dom/DocumentInlines.h" ++#include "mozilla/nsZenDragAndDrop.h" + #include "nsIContent.h" + #include "nsCocoaUtils.h" + #include "mozilla/gfx/2D.h" +@@ -148,6 +149,10 @@ + bitsPerPixel:32]; + + uint8_t* dest = [imageRep bitmapData]; ++ auto drag_translucency = DRAG_TRANSLUCENCY; ++ if (auto zenDragAndDrop = zen::nsZenDragAndDrop::GetZenDragAndDropInstance()) { ++ drag_translucency = zenDragAndDrop->GetDragImageOpacity(); ++ } + for (uint32_t i = 0; i < height; ++i) { + uint8_t* src = map.mData + i * map.mStride; + for (uint32_t j = 0; j < width; ++j) { +@@ -155,15 +160,15 @@ + // is premultipled here. Also, Quartz likes RGBA, so do that translation + // as well. + #ifdef IS_BIG_ENDIAN +- dest[0] = uint8_t(src[1] * DRAG_TRANSLUCENCY); +- dest[1] = uint8_t(src[2] * DRAG_TRANSLUCENCY); +- dest[2] = uint8_t(src[3] * DRAG_TRANSLUCENCY); +- dest[3] = uint8_t(src[0] * DRAG_TRANSLUCENCY); ++ dest[0] = uint8_t(src[1] * drag_translucency); ++ dest[1] = uint8_t(src[2] * drag_translucency); ++ dest[2] = uint8_t(src[3] * drag_translucency); ++ dest[3] = uint8_t(src[0] * drag_translucency); + #else +- dest[0] = uint8_t(src[2] * DRAG_TRANSLUCENCY); +- dest[1] = uint8_t(src[1] * DRAG_TRANSLUCENCY); +- dest[2] = uint8_t(src[0] * DRAG_TRANSLUCENCY); +- dest[3] = uint8_t(src[3] * DRAG_TRANSLUCENCY); ++ dest[0] = uint8_t(src[2] * drag_translucency); ++ dest[1] = uint8_t(src[1] * drag_translucency); ++ dest[2] = uint8_t(src[0] * drag_translucency); ++ dest[3] = uint8_t(src[3] * drag_translucency); + #endif + src += 4; + dest += 4; diff --git a/src/zen/common/jar.inc.mn b/src/zen/common/jar.inc.mn index e8f2355c7..2d4172f8b 100644 --- a/src/zen/common/jar.inc.mn +++ b/src/zen/common/jar.inc.mn @@ -13,7 +13,6 @@ 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/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/ZenEmojiPicker.mjs (../../zen/common/emojis/ZenEmojiPicker.mjs) diff --git a/src/zen/common/ZenDragAndDrop.js b/src/zen/drag-and-drop/ZenDragAndDrop.js similarity index 96% rename from src/zen/common/ZenDragAndDrop.js rename to src/zen/drag-and-drop/ZenDragAndDrop.js index 71bc848fc..2aecc213b 100644 --- a/src/zen/common/ZenDragAndDrop.js +++ b/src/zen/drag-and-drop/ZenDragAndDrop.js @@ -32,7 +32,7 @@ * @returns {MozTabbrowserTab|vbox} */ const elementToMove = (element) => { - if (element.classList.contains('zen-current-workspace-indicator')) { + if (element.closest('.zen-current-workspace-indicator')) { return element; } if (element.group?.hasAttribute('split-view-group')) { @@ -50,17 +50,28 @@ window.ZenDragAndDrop = class extends window.TabDragAndDrop { #dragOverBackground = null; #lastDropTarget = null; + originalDragImageArgs = []; constructor(tabbrowserTabs) { super(tabbrowserTabs); + + XPCOMUtils.defineLazyServiceGetter( + this, + 'ZenDragAndDropService', + '@mozilla.org/zen/drag-and-drop;1', + Ci.nsIZenDragAndDrop + ); } startTabDrag(event, tab, ...args) { + this.ZenDragAndDropService.onDragStart(1); + super.startTabDrag(event, tab, ...args); const dt = event.dataTransfer; const { offsetX, offsetY } = this.#getDragImageOffset(event, tab); - dt.setDragImage(tab, offsetX, offsetY); + this.originalDragImageArgs = [tab, offsetX, offsetY]; + dt.setDragImage(...this.originalDragImageArgs); } _animateTabMove(event) { @@ -460,9 +471,11 @@ } handle_dragend(event) { + this.ZenDragAndDropService.onDragEnd(); super.handle_dragend(event); this.#removeDragOverBackground(); gZenPinnedTabManager.removeTabContainersDragoverClass(); + this.originalDragImageArgs = []; } #applyDragOverBackground(element) { @@ -488,6 +501,11 @@ } } + clearDragOverVisuals() { + this.#removeDragOverBackground(); + gZenPinnedTabManager.removeTabContainersDragoverClass(); + } + #applyDragoverIndicator(event, tabs, movingTabs) { const separation = 4; const dropZoneSelector = ':is(.tabbrowser-tab, .zen-drop-target, .tab-group-label)'; @@ -499,8 +517,7 @@ const numPinned = gBrowser.pinnedTabCount - numEssentials; const tabToUse = event.target.closest(dropZoneSelector); if (!tabToUse) { - this.#removeDragOverBackground(); - gZenPinnedTabManager.removeTabContainersDragoverClass(); + this.clearDragOverVisuals(); return; } const isPinned = tabToUse.pinned; @@ -509,6 +526,10 @@ dropElement = event.clientY > draggedTabRect.top ? relativeTabs.at(-1) : relativeTabs[0]; } dropElement = elementToMove(dropElement); + if (this._isContainerVerticalPinnedGrid(dropElement)) { + this._animateExpandedPinnedTabMove(event); + return; + } if (this.#lastDropTarget !== dropElement) { shouldPlayHapticFeedback = this.#lastDropTarget !== null; this.#removeDragOverBackground(); @@ -522,8 +543,8 @@ // 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); - if (isTab(dropElement) || !dropIntoFolder) { + isZenFolder && (overlapPercent < threshold || overlapPercent > 1 - threshold); + if (isTab(dropElement) || dropIntoFolder) { const indicator = gZenPinnedTabManager.dragIndicator; let top = 0; threshold = diff --git a/src/zen/drag-and-drop/components.conf b/src/zen/drag-and-drop/components.conf new file mode 100644 index 000000000..576fc3af0 --- /dev/null +++ b/src/zen/drag-and-drop/components.conf @@ -0,0 +1,14 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Classes = [ + { + 'cid': '{f8714110-1fb1-4129-abad-887a64e4085e}', + 'interfaces': ['nsIZenDragAndDrop'], + 'contract_ids': ['@mozilla.org/zen/drag-and-drop;1'], + 'type': 'zen::nsZenDragAndDrop', + 'headers': ['mozilla/nsZenDragAndDrop.h'], + 'processes': ProcessSelector.MAIN_PROCESS_ONLY, + }, +] diff --git a/src/zen/drag-and-drop/jar.inc.mn b/src/zen/drag-and-drop/jar.inc.mn new file mode 100644 index 000000000..d5c31bbbf --- /dev/null +++ b/src/zen/drag-and-drop/jar.inc.mn @@ -0,0 +1,5 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + content/browser/zen-components/ZenDragAndDrop.js (../../zen/drag-and-drop/ZenDragAndDrop.js) diff --git a/src/zen/drag-and-drop/moz.build b/src/zen/drag-and-drop/moz.build new file mode 100644 index 000000000..9b7aa4564 --- /dev/null +++ b/src/zen/drag-and-drop/moz.build @@ -0,0 +1,31 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPIDL_SOURCES += [ + "nsIZenDragAndDrop.idl", +] + +EXPORTS.mozilla += [ + "nsZenDragAndDrop.h", +] + +SOURCES += [ + "nsZenDragAndDrop.cpp", +] + +XPCOM_MANIFESTS += [ + "components.conf", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +LOCAL_INCLUDES += [ + "/dom/base", + "/layout/base", + "/widget", +] + +FINAL_LIBRARY = "xul" +XPIDL_MODULE = "zen_dnd" diff --git a/src/zen/drag-and-drop/nsIZenDragAndDrop.idl b/src/zen/drag-and-drop/nsIZenDragAndDrop.idl new file mode 100644 index 000000000..c8fddbf20 --- /dev/null +++ b/src/zen/drag-and-drop/nsIZenDragAndDrop.idl @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * @brief Interface for Zen's drag and drop functionality. + */ +[scriptable, uuid(f8714110-1fb1-4129-abad-887a64e4085e)] +interface nsIZenDragAndDrop : nsISupports { + /** + * @brief Indicate that a drag operation has started. Note + * that this should only be called for zen's drag and drop + * operations for the tabs. + * @param opacity The opacity of the drag image. + */ + void onDragStart(in float opacity); + + /** + * @brief Indicate that a drag operation has ended. + */ + void onDragEnd(); +}; + diff --git a/src/zen/drag-and-drop/nsZenDragAndDrop.cpp b/src/zen/drag-and-drop/nsZenDragAndDrop.cpp new file mode 100644 index 000000000..48246e842 --- /dev/null +++ b/src/zen/drag-and-drop/nsZenDragAndDrop.cpp @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsZenDragAndDrop.h" +#include "nsBaseDragService.h" + +namespace zen { +namespace { + +static constexpr auto kZenDefaultDragImageOpacity = +#if defined(MOZ_WIDGET_GTK) +// For GTK, the default is 0.5 (DRAG_IMAGE_ALPHA_LEVEL) to match +// the native behavior. Make sure its synced with the following variable: +// https://searchfox.org/firefox-main/rev/14c08f0368ead8bfdddec62f43e0bb5c8fd61289/widget/gtk/nsDragService.cpp#75 + 0.5f; +#else +// For other platforms, the default is whatever the value of DRAG_TRANSLUCENCY +// is, defined in nsBaseDragService.h + DRAG_TRANSLUCENCY; +#endif + +} // namespace: + +// Use the macro to inject all of the definitions for nsISupports. +NS_IMPL_ISUPPORTS(nsZenDragAndDrop, nsIZenDragAndDrop) + +nsZenDragAndDrop::nsZenDragAndDrop() { + (void)this->OnDragEnd(); +} + +auto nsZenDragAndDrop::GetZenDragAndDropInstance() -> nsCOMPtr { + return do_GetService(ZEN_BOOSTS_BACKEND_CONTRACTID); +} + +NS_IMETHODIMP +nsZenDragAndDrop::OnDragStart(float opacity) { + mDragImageOpacity = opacity; + return NS_OK; +} + +NS_IMETHODIMP +nsZenDragAndDrop::OnDragEnd() { + mDragImageOpacity = kZenDefaultDragImageOpacity; + return NS_OK; +} + +} // namespace: zen diff --git a/src/zen/drag-and-drop/nsZenDragAndDrop.h b/src/zen/drag-and-drop/nsZenDragAndDrop.h new file mode 100644 index 000000000..d03990bfa --- /dev/null +++ b/src/zen/drag-and-drop/nsZenDragAndDrop.h @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ZenDragAndDrop_h__ +#define mozilla_ZenDragAndDrop_h__ + +#include "nsIZenDragAndDrop.h" +#include "nsCOMPtr.h" + +#define ZEN_BOOSTS_BACKEND_CONTRACTID "@mozilla.org/zen/drag-and-drop;1" + +namespace zen { + +/** + * @brief Implementation of the nsIZenDragAndDrop interface. + * When we want to do a drag and drop operation, web standards + * don't really allow much customization of the drag image. + * This class allows Zen to have more control over the drag + * and drop operations for the tabs. + */ +class nsZenDragAndDrop final : public nsIZenDragAndDrop { + NS_DECL_ISUPPORTS + NS_DECL_NSIZENDRAGANDDROP + + public: + explicit nsZenDragAndDrop(); + auto GetDragImageOpacity() const { return mDragImageOpacity; } + + /** + * @brief Get the singleton instance of nsZenDragAndDrop. There may be occasions + * where it won't be available (e.g. on the content process), so this may return + * nullptr. + * @return nsZenDragAndDrop* The singleton instance, or nullptr if not available + */ + static auto GetZenDragAndDropInstance() -> nsCOMPtr; + + private: + ~nsZenDragAndDrop() = default; + float mDragImageOpacity{}; +}; + +} // namespace zen + +#endif diff --git a/src/zen/moz.build b/src/zen/moz.build index eb681597f..4910bc43d 100644 --- a/src/zen/moz.build +++ b/src/zen/moz.build @@ -8,6 +8,7 @@ EXTRA_PP_COMPONENTS += [ DIRS += [ "common", + "drag-and-drop", "glance", "mods", "tests", diff --git a/src/zen/split-view/ZenViewSplitter.mjs b/src/zen/split-view/ZenViewSplitter.mjs index 8fd03aa9a..03364d5be 100644 --- a/src/zen/split-view/ZenViewSplitter.mjs +++ b/src/zen/split-view/ZenViewSplitter.mjs @@ -228,6 +228,24 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { } } + #getDragImageForSplit(tab) { + const element = window.MozXULElement.parseXULToFragment( + ` + + + + ` + ).querySelector('#zen-split-view-drag-image'); + const image = element.querySelector('image'); + const label = element.querySelector('label'); + image.src = tab.getAttribute('image'); + label.textContent = tab.label; + document.documentElement.appendChild(element); + this._dndElement = element; + return element; + } + onBrowserDragOverToSplit(event) { if (this.fakeBrowser) { this.onBrowserDragEndToSplit(event); @@ -285,6 +303,17 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { return; } dt.mozCursor = 'default'; + if (!this._dndElement) { + const originalDNDArgs = gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs; + requestAnimationFrame(() => { + dt.updateDragImage( + this.#getDragImageForSplit(draggedTab), + originalDNDArgs[1], + originalDNDArgs[2] + ); + }); + gBrowser.tabContainer.tabDragAndDrop.clearDragOverVisuals(); + } const oldTab = this._lastOpenedTab; this._canDrop = true; { @@ -330,11 +359,6 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { this.fakeBrowser.setAttribute('has-split-view', 'true'); } gBrowser.tabbox.appendChild(this.fakeBrowser); - this.fakeBrowser.style.setProperty( - '--zen-split-view-fake-icon', - `url(${draggedTab.getAttribute('image')})` - ); - draggedTab._visuallySelected = true; this.fakeBrowser.setAttribute('side', side); this._finishAllAnimatingPromise = Promise.all([ gZenUIManager.motion.animate( @@ -377,7 +401,6 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { .classList.remove('deck-selected'); this.fakeBrowser.addEventListener('dragleave', this.onBrowserDragEndToSplit); this._canDrop = true; - draggedTab._visuallySelected = true; }); } } @@ -390,12 +413,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { const panelsRect = gBrowser.tabbox.getBoundingClientRect(); const fakeBrowserRect = this.fakeBrowser && this.fakeBrowser.getBoundingClientRect(); if ( - ((event.target.closest('#tabbrowser-tabbox') && event.target != this.fakeBrowser) || - (fakeBrowserRect && - event.clientX > fakeBrowserRect.left && - event.clientX < fakeBrowserRect.left + fakeBrowserRect.width && - event.clientY > fakeBrowserRect.top && - event.clientY < fakeBrowserRect.top + fakeBrowserRect.height) || + ((fakeBrowserRect && + event.clientX > fakeBrowserRect.left && + event.clientX < fakeBrowserRect.left + fakeBrowserRect.width && + event.clientY > fakeBrowserRect.top && + event.clientY < fakeBrowserRect.top + fakeBrowserRect.height) || (event.screenX === 0 && event.screenY === 0)) && // It's equivalent to 0 if the event has been dropped !cancelled ) { @@ -415,50 +437,47 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { if (!this.fakeBrowser) { return; } - this.fakeBrowser.classList.add('fade-out'); const side = this.fakeBrowser.getAttribute('side'); if (this._draggingTab) this._draggingTab.setAttribute('zen-has-splitted', 'true'); this._lastOpenedTab = gBrowser.selectedTab; this._draggingTab = null; - try { - this._canDrop = false; - Promise.all([ - gZenUIManager.motion.animate( - gBrowser.tabbox, - side === 'left' + event.dataTransfer.updateDragImage( + ...gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs + ); + this._canDrop = false; + Promise.all([ + gZenUIManager.motion.animate( + gBrowser.tabbox, + side === 'left' + ? { + paddingLeft: [`${halfWidth}px`, 0], + } + : { + paddingRight: [`${halfWidth}px`, 0], + }, + { + duration: 0.1, + easing: 'ease-out', + } + ), + gZenUIManager.motion.animate( + this.fakeBrowser, + { + width: [`${halfWidth - padding * 2}px`, 0], + ...(side === 'left' ? { - paddingLeft: [`${halfWidth}px`, 0], + marginLeft: [`${-halfWidth}px`, 0], } - : { - paddingRight: [`${halfWidth}px`, 0], - }, - { - duration: 0.1, - easing: 'ease-out', - } - ), - gZenUIManager.motion.animate( - this.fakeBrowser, - { - width: [`${halfWidth - padding * 2}px`, 0], - ...(side === 'left' - ? { - marginLeft: [`${-halfWidth}px`, 0], - } - : {}), - }, - { - duration: 0.1, - easing: 'ease-out', - } - ), - ]).then(() => { - this._maybeRemoveFakeBrowser(); - }); - } catch { - this._canDrop = false; + : {}), + }, + { + duration: 0.1, + easing: 'ease-out', + } + ), + ]).finally(() => { this._maybeRemoveFakeBrowser(); - } + }); } /** @@ -1659,11 +1678,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { _maybeRemoveFakeBrowser(select = true) { gBrowser.tabbox.removeAttribute('style'); this.tabBrowserPanel.removeAttribute('dragging-split'); + if (this._dndElement) { + this._dndElement.remove(); + delete this._dndElement; + } if (this.fakeBrowser) { delete this._hasAnimated; this.fakeBrowser.remove(); this.fakeBrowser = null; - if (this._draggingTab) this._draggingTab._visuallySelected = false; if (select) { gBrowser.selectedTab = this._draggingTab; this._draggingTab = null; diff --git a/src/zen/split-view/zen-decks.css b/src/zen/split-view/zen-decks.css index 9c4ff3f3a..861460bb1 100644 --- a/src/zen/split-view/zen-decks.css +++ b/src/zen/split-view/zen-decks.css @@ -210,31 +210,28 @@ right: var(--zen-element-separation); } } +} - &::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 3.5rem; - pointer-events: none; - height: 3.5rem; - background: var(--zen-split-view-fake-icon); - background-size: contain; - background-repeat: no-repeat; - background-position: center; - opacity: 0.8; - transition: opacity 0.2s; - transition-delay: 0.1s; +#zen-split-view-drag-image { + width: 200px; + height: 250px; + border-radius: 16px; + background: black; + justify-content: center; + align-items: center; + padding: 10px; + gap: 20px; + position: relative; - @starting-style { - opacity: 0; - } + & image { + width: 24px; + height: 24px; } - &.fade-out::after { - opacity: 0; - transition-delay: 0s; + & label { + color: white; + font-size: 14px; + font-weight: bold; + text-align: center; } } diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index b2b5d503a..6dd432c90 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -1366,11 +1366,6 @@ } } -.tabbrowser-tab[zen-dragtarget], -.tab-group-label-container[zen-dragtarget] { - z-index: 9 !important; -} - #zen-dragover-background { position: absolute; z-index: -1;