feat: Add dnd switch support for groups, p=#11854

* feat: Add dnd switch support for groups

* refactor: Use `changeFolderToSpace` for drag-and-drop workspace changes

* refactor: Use optional parameter instead of attribute

* fix: Formatting

* refactor: Move condition inside function

* Refactor tab group handling in drag-and-drop

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

* feat: Only allow double click rename on labels, b=no-bug, c=common

---------

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
This commit is contained in:
Andrey Bochkarev
2026-01-10 16:41:28 +03:00
committed by GitHub
parent 44a28e2afe
commit fc7f10aef1
6 changed files with 56 additions and 21 deletions

View File

@@ -32,6 +32,9 @@
- name: zen.workspaces.separate-essentials
value: true
- name: zen.workspaces.dnd-switch-padding
value: 10
- name: zen.workspaces.debug
value: '@cond'
condition: '!defined(MOZILLA_OFFICIAL)' # Section: Pinned tabs management

View File

@@ -1389,8 +1389,12 @@ window.gZenVerticalTabsManager = {
Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick')) &&
isTab) ||
!gZenVerticalTabsManager._prefsSidebarExpanded
)
) {
return;
}
if (isTab && !target.closest('.tab-label-container')) {
return;
}
this._tabEdited =
target.closest('.tabbrowser-tab') ||
target.closest('.zen-current-workspace-indicator-name') ||

View File

@@ -577,7 +577,7 @@
}
#shouldSwitchSpace(event) {
const padding = 10;
const padding = Services.prefs.getIntPref('zen.workspaces.dnd-switch-padding');
// If we are hovering over the edges of the gNavToolbox or the splitter, we
// can change the workspace after a short delay.
const splitter = document.getElementById('zen-sidebar-splitter');
@@ -601,8 +601,7 @@
#handle_sidebarDragOver(event) {
const dt = event.dataTransfer;
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
// TODO: Add support for switching spaces when dragging folders and split-view groups.
if (!isTab(draggedTab) || draggedTab.hasAttribute('zen-essential')) {
if (draggedTab.hasAttribute('zen-essential')) {
this.clearSpaceSwitchTimer();
return;
}
@@ -683,18 +682,23 @@
this.clearSpaceSwitchTimer();
super.handle_drop(event);
const dt = event.dataTransfer;
const activeWorkspace = gZenWorkspaces.activeWorkspace;
let draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (
isTab(draggedTab) &&
!draggedTab.hasAttribute('zen-essential') &&
draggedTab.getAttribute('zen-workspace-id') != gZenWorkspaces.activeWorkspace
draggedTab.getAttribute('zen-workspace-id') != activeWorkspace
) {
const movingTabs = draggedTab._dragData?.movingTabs || [draggedTab];
for (let tab of movingTabs) {
tab.setAttribute('zen-workspace-id', gZenWorkspaces.activeWorkspace);
tab.setAttribute('zen-workspace-id', activeWorkspace);
}
gBrowser.selectedTab = draggedTab;
}
if (isTabGroupLabel(draggedTab)) {
draggedTab = draggedTab.group;
gZenFolders.changeFolderToSpace(draggedTab, activeWorkspace, { hasDndSwitch: true });
}
gZenWorkspaces.updateTabsContainers();
}

View File

@@ -442,29 +442,40 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
}
changeFolderToSpace(folder, workspaceId) {
const currentWorkspace = gZenWorkspaces.getActiveWorkspaceFromCache();
if (currentWorkspace.uuid === workspaceId) {
changeFolderToSpace(folder, workspaceId, { hasDndSwitch = false } = {}) {
if (folder.getAttribute('zen-workspace-id') == workspaceId) {
return;
}
const workspaceElement = gZenWorkspaces.workspaceElement(workspaceId);
const pinnedTabsContainer = workspaceElement.pinnedTabsContainer;
pinnedTabsContainer.insertBefore(folder, pinnedTabsContainer.lastChild);
if (!hasDndSwitch) {
const pinnedTabsContainer = workspaceElement.pinnedTabsContainer;
pinnedTabsContainer.insertBefore(folder, pinnedTabsContainer.lastChild);
}
const { lastSelectedWorkspaceTabs } = gZenWorkspaces;
for (const tab of folder.tabs) {
tab.setAttribute('zen-workspace-id', workspaceId);
// This sets the ID for the current folder and any sub-folder
// we may encounter
tab.setAttribute('zen-workspace-id', workspaceId);
tab.group.setAttribute('zen-workspace-id', workspaceId);
gBrowser.TabStateFlusher.flush(tab.linkedBrowser);
if (gZenWorkspaces.lastSelectedWorkspaceTabs[workspaceId] === tab) {
if (lastSelectedWorkspaceTabs[workspaceId] === tab) {
// This tab is no longer the last selected tab in the previous workspace because it's being moved to a new workspace
delete gZenWorkspaces.lastSelectedWorkspaceTabs[workspaceId];
delete lastSelectedWorkspaceTabs[workspaceId];
}
}
folder.dispatchEvent(new CustomEvent('ZenFolderChangedWorkspace', { bubbles: true }));
gZenWorkspaces.changeWorkspaceWithID(workspaceId).then(() => {
gBrowser.moveTabTo(folder, { elementIndex: 0, forceUngrouped: true });
});
if (!hasDndSwitch) {
gZenWorkspaces.changeWorkspaceWithID(workspaceId).then(() => {
gBrowser.moveTabTo(folder, { elementIndex: 0, forceUngrouped: true });
});
}
}
canDropElement(element, targetElement) {

View File

@@ -461,7 +461,7 @@ class nsZenWindowSync {
!originalSibling.hasAttribute('id') || originalSibling.hasAttribute('zen-empty-tab');
}
gBrowser.zenHandleTabMove(aOriginalItem, () => {
gBrowser.zenHandleTabMove(aTargetItem, () => {
if (isFirstTab) {
let container;
const parentGroup = aOriginalItem.group;
@@ -1062,6 +1062,15 @@ class nsZenWindowSync {
}
// Tab groups already have an ID upon creation.
this.#runOnAllWindows(window, (win) => {
// Check if a group with this ID already exists in the target window.
const existingGroup = this.getItemFromWindow(win, tabGroup.id);
if (existingGroup) {
this.log(
`Attempted to create group ${tabGroup.id} in window ${win}, ` + `but it already exists.`
);
return; // Do not proceed with creation.
}
const newGroup = isFolder
? win.gZenFolders.createFolder([], {})
: win.gBrowser.addTabGroup([]);

View File

@@ -685,8 +685,11 @@ class nsZenWorkspaces {
const delta = event.delta * 300;
const stripWidth =
window.windowUtils.getBoundsWithoutFlushing(document.getElementById('navigator-toolbox')).width +
window.windowUtils.getBoundsWithoutFlushing(document.getElementById('zen-sidebar-splitter')).width * 2;
window.windowUtils.getBoundsWithoutFlushing(document.getElementById('navigator-toolbox'))
.width +
window.windowUtils.getBoundsWithoutFlushing(document.getElementById('zen-sidebar-splitter'))
.width *
2;
let translateX = this._swipeState.lastDelta + delta;
// Add a force multiplier as we are translating the strip depending on how close to the edge we are
let forceMultiplier = Math.min(1, 1 - Math.abs(translateX) / (stripWidth * 4.5)); // 4.5 instead of 4 to add a bit of a buffer
@@ -1681,7 +1684,8 @@ class nsZenWorkspaces {
!(this.#inChangingWorkspace && !forAnimation && !this._alwaysAnimatePaddingTop)
) {
delete this._alwaysAnimatePaddingTop;
const essentialsHeight = window.windowUtils.getBoundsWithoutFlushing(essentialContainer).height;
const essentialsHeight =
window.windowUtils.getBoundsWithoutFlushing(essentialContainer).height;
if (!forAnimation && animateContainer && gZenUIManager.motion && gZenStartup.isReady) {
gZenUIManager.motion.animate(
workspaceElement,