Enhance Zen Tab Reordering: Add drag-and-drop between tab containers (normal tabs, pinned and essentials)

This commit enhances the drag-and-drop functionality in Zen, allowing users to seamlessly reorder tabs between the pinned tabs, essential tabs, and regular tab containers.  The changes include:

- Added `moveToAnotherTabContainerIfNecessary` and `applyDragoverClass` methods to `ZenPinnedTabManager` to handle tab movement and visual feedback during drag-and-drop.
- Updated `tabs.js` to integrate with the new methods, correctly handling tab pinning and unpinning, and essential tab toggling during reordering.
- Added CSS classes and styles to visually indicate the drop position (before or after) a target tab during the drag operation.  This improves user experience and clarity.
This commit is contained in:
Kristijan Ribarić
2025-01-31 17:49:59 +01:00
parent 89d1f7a731
commit d3a04d043b
2 changed files with 157 additions and 4 deletions

View File

@@ -1069,3 +1069,29 @@
%include vertical-tabs-topbuttons-fix.css
}
}
#vertical-pinned-tabs-container .tabbrowser-tab,
#tabbrowser-arrowscrollbox .tabbrowser-tab,
#zen-essentials-container .tabbrowser-tab {
transition: box-shadow 0.2s ease-in-out;
}
/* Vertical tabs reordering indicators */
#vertical-pinned-tabs-container .tabbrowser-tab.drag-over-before,
#tabbrowser-arrowscrollbox .tabbrowser-tab.drag-over-before {
box-shadow: 0 3px 6px -2px var(--toolbarbutton-active-background, rgba(0, 0, 255, 0.2));
}
#vertical-pinned-tabs-container .tabbrowser-tab.drag-over-after,
#tabbrowser-arrowscrollbox .tabbrowser-tab.drag-over-after {
box-shadow: 0 -3px 6px -2px var(--toolbarbutton-active-background, rgba(0, 0, 255, 0.2));
}
/* Horizontal tabs reordering indicators */
#zen-essentials-container .tabbrowser-tab.drag-over-before {
box-shadow: 3px 0 6px -2px var(--toolbarbutton-active-background, rgba(0, 255, 0, 0.2));
}
#zen-essentials-container .tabbrowser-tab.drag-over-after {
box-shadow: -3px 0 6px -2px var(--toolbarbutton-active-background, rgba(0, 255, 0, 0.2));
}

View File

@@ -557,8 +557,8 @@
}
}
addToEssentials() {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
addToEssentials(tab) {
const tabs = tab ? [tab] : TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.setAttribute('zen-essential', 'true');
@@ -575,8 +575,8 @@
gZenUIManager.updateTabsToolbar();
}
removeEssentials() {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
removeEssentials(tab) {
const tabs = tab ? [tab] : TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.removeAttribute('zen-essential');
@@ -640,6 +640,133 @@
document.getElementById('context_unpinSelectedTabs').hidden || contextTab.getAttribute('zen-essential');
document.getElementById('context_zen-pinned-tab-separator').hidden = !isVisible;
}
moveToAnotherTabContainerIfNecessary(event, draggedTab) {
const pinnedTabsTarget = event.target.closest("#vertical-pinned-tabs-container");
const essentialTabsTarget = event.target.closest("#zen-essentials-container");
const tabsTarget = event.target.closest("#tabbrowser-arrowscrollbox");
let moved = false;
let isVertical = true;
// Check for pinned tabs container
if (pinnedTabsTarget) {
if (!draggedTab.pinned) {
gBrowser.pinTab(draggedTab);
moved = true;
} else if (draggedTab.hasAttribute("zen-essential")) {
this.removeEssentials(draggedTab);
gBrowser.pinTab(draggedTab);
moved = true;
}
}
// Check for essentials container
else if (essentialTabsTarget) {
if (!draggedTab.hasAttribute("zen-essential")) {
this.addToEssentials(draggedTab);
moved = true;
isVertical = false;
}
}
// Check for normal tabs container
else if (tabsTarget) {
if (draggedTab.pinned && !draggedTab.hasAttribute("zen-essential")) {
gBrowser.unpinTab(draggedTab);
moved = true;
} else if (draggedTab.hasAttribute("zen-essential")) {
this.removeEssentials(draggedTab);
moved = true;
}
}
// If the tab was moved, adjust its position relative to the target tab
if (moved) {
const targetTab = event.target.closest(".tabbrowser-tab");
if (targetTab) {
const rect = targetTab.getBoundingClientRect();
let newIndex = targetTab._tPos;
if (isVertical) {
const middleY = targetTab.screenY + rect.height / 2;
if (event.screenY > middleY) {
newIndex++;
}
} else {
const middleX = targetTab.screenX + rect.width / 2;
if (event.screenX > middleX) {
newIndex++;
}
}
gBrowser.moveTabTo(draggedTab, newIndex);
}
}
return moved;
}
removeTabContainersDragoverClass() {
document
.querySelectorAll(".tabbrowser-tab.drag-over-before, .tabbrowser-tab.drag-over-after")
.forEach(tab => {
tab.classList.remove("drag-over-before", "drag-over-after");
});
}
applyDragoverClass(event, draggedTab) {
this.removeTabContainersDragoverClass();
const pinnedTabsTarget = event.target.closest("#vertical-pinned-tabs-container");
const essentialTabsTarget = event.target.closest("#zen-essentials-container");
const tabsTarget = event.target.closest("#tabbrowser-arrowscrollbox");
const targetTab = event.target.closest(".tabbrowser-tab");
// If there's no valid target tab, nothing to do
if (!targetTab) {
return;
}
let shouldAddDragOverElement = false;
let isVertical = true;
// Decide whether we should show a dragover class for the given target
if (pinnedTabsTarget) {
if (!draggedTab.pinned || draggedTab.hasAttribute("zen-essential")) {
shouldAddDragOverElement = true;
}
} else if (essentialTabsTarget) {
if (!draggedTab.hasAttribute("zen-essential")) {
shouldAddDragOverElement = true;
isVertical = false;
}
} else if (tabsTarget) {
if (draggedTab.pinned || draggedTab.hasAttribute("zen-essential")) {
shouldAddDragOverElement = true;
}
}
if (!shouldAddDragOverElement) {
return;
}
// Calculate middle to decide 'before' or 'after'
const rect = targetTab.getBoundingClientRect();
if (isVertical) {
const middleY = targetTab.screenY + rect.height / 2;
if (event.screenY > middleY) {
targetTab.classList.add("drag-over-before");
} else {
targetTab.classList.add("drag-over-after");
}
} else {
const middleX = targetTab.screenX + rect.width / 2;
if (event.screenX > middleX) {
targetTab.classList.add("drag-over-before");
} else {
targetTab.classList.add("drag-over-after");
}
}
}
}
window.gZenPinnedTabManager = new ZenPinnedTabManager();