feat: Zen Folders, p=#9355, c=folders

* Start working on zen folders

* Rework zen-folder SessionStore

* Refactor restoreDataFromSessionStore

* fix linter

* Fix preserve folder order on restore

* Feat allow dragging tabs into zen-folder

* Fix ensure collapsed folders are hidden on session restore

* Feat store parentId nested folders

* feat: Implement tabs list popup

* refactor: Move tabs popup to `popups.inc`

* feat: Implement drag-and-drop folder into folder

* feat: Improved UI for search panel, b=no-bug, c=folders

* fix: Add extra margin when animating collapsed folders, b=no-bug, c=folders

* feat: Implement tab group rename and other UI changes, b=no-bug, c=folders, common

* feat: Add animated folder dots and adaptive search popup positioning

* fix: resolve conflicts

* fix: Correct active state indication for collapsed folders

* feat: Allow folders to be double clicked, b=no-bug, c=common, folders

* fix: incorrect tab order

* chore: Update prefs to the rust version, b=no-bug, c=folders

* fix: better handling of subfolders

* chore: Improve dynamic spacing when drag and dropping and fixed split views UI, b=no-bug, c=tabs, folders

* feat: Empty tab and improve drag and drop

* fix: add tab search event once

* fix: Empty tab should always be at first position

* feat: improve drag and drop interaction with folders

* feat: Improve drag-and-drop interaction for zen folders

* fix: Improve zen folder session restoration and visibility

* fix: Correct visible element indexing

* fix: Correct restore subfolder order

* feat: Use empty tabs and dont highlight current folder we currently are in, b=no-bug, c=tabs, folders

* feat: persist and restore split-view group state in subfolders

* fix: npm run pretty

* fix: dropIndicator and transform for split-view-group

* fix: Formatting

* fix: improve split group and folder drag-and-drop and persistence

* chore: Fix lint issues and merge with dev, b=no-bug, c=folders

* chore: Move folder element to a different location, b=no-bug, c=folders

* feat: Added a simple folders context menu and simplified patches, b=no-bug, c=tabs, folders

* fix: Correct active tab position in folders collapse animation

* feat: Add ungroup and delete folder actions

* fix: Fixed empty tabs not being able to be pinned, b=no-bug, c=workspaces

* feat: Added folder -> space conversion and pref checks, b=no-bug, c=folders, workspaces

* Update locales/en-US/browser/browser/zen-folders.ftl

Co-authored-by: Patrik Egyed <pregnor@gmail.com>
Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

* fix: Fixed folders not expanding when dragging another folder inside, b=no-bug, c=folders

* refactor: Refactor and improve tab group expansion logic

* feat: New folder dots

* test: Added simple folder creation test, b=no-bug, c=folders, tests

* fix: Don't expand folder when a tab inside it is selected

* feat: Added change folder to space menu item, b=no-bug, c=folders, workspaces

* feat: Added a menu item to create folders more easily, b=no-bug, c=workspaces, common, folders

* feat: Improved animations for collapsing active folders, b=no-bug, c=folders

* fix: Insert folder before pinned separator

* test: Improve folder and welcome testing, b=no-bug, c=folders, tests, welcome

* test: Fixed welcome tests, b=no-bug, c=folders, tests, welcome

* chore: lint, b=no-bug, c=tests, welcome

* feat: Add better selected UI, b=no-bug, c=folders

* feat: Emoji icons

* fix: Better handling of drag-and-drop folder highlighting

* fix: Single quotes

* fix: Hide emoji when folder has selected tab

* feat: Improved icons and animations, b=no-bug, c=folders, tabs

* fix: Fixed fetching the wrong prefs, b=no-bug, c=tabs, folders

* fix: Smoother dot animations

* fix: dragOverFolderThreshold condition and linter

* feat: visually collapse/expand active tab groups on drag/drop

* fix: Correctly transform folder with selected tab

* feat: Added better icons picker to support SVG, b=no-bug, c=common, folders, workspaces

* fix: Correctly transform tabs after moving them

* fix: Fixed not handling properly pinned tab count, b=no-bug, c=tabs, common, folders

* chore: Small formatting, b=no-bug, c=folders

* feat: Support SVG for folder icons

* fix: Formatting

* fix: Performance improvements for SVG icons

* fix: Shift up the folder icon

* fix: Handle null/undefined user icon values defensively

* feat: Improved icon sizes and fixed bug when collapsing folders with collapsed folders, b=no-bug, c=common, folders, workspaces

* chore: Tweaked the transform values for icons, b=no-bug, c=folders

* feat: Added support for collapsed mode (experimental), b=no-bug, c=folders

* fix: ungroup split view

* fix: Improve handling of special tabs during folder creation and drag-and-drop

* fix: Formatting

* feat: Imrpoved hardware accelaration for the icons and folder height calculation, b=no-bug, c=tabs, folders

* refactor: Extract dragover logic for tab group labels

* fix: Small fixes to the folders UI, b=no-bug, c=tabs, folders

* feat: Improved icons opacity and dialog, b=no-bug, c=common, workspaces

* test: Added subfolders basic test, b=no-bug, c=folders, tests

* fix: Drop indicator for folder targets

* feat: Improved drag and drop handling from normal to pinned tabs, b=no-bug, c=folders, tabs

* fix: Fixed moving split views into pinned tabs container, b=no-bug, c=folders, tabs

* feat: Improved new drag and drop offset, b=no-bug, c=tabs

* feat: Refine folder drop behavior with new thresholds

* fix: tabs.js extra space in patch

* fix: Properly handle has-active state

* fix: Add optional chaining for activeGroups length check

* fix: Fixed moving tabs to the workspace indicator not showing any feedback, b=no-bug, c=tabs

* feat: Change svg stroke width, b=no-bug, c=folders, tabs

* feat: Remove aspect ratio for the folder icon, b=no-bug, c=folders, tabs

* feat: Don't reset transform when pining tabs, b=no-bug, c=tabs

* feat: Ungroup tabs when dragging and make sure to animate tabs after the selected one, b=no-bug, c=tabs, folders

* fix: Transform folder with active tab

* fix: Fixed expand animation not working for the first time, b=no-bug, c=folders

* feat: Add expand to selected functionality for folders

* fix: Formatting

* Update src/zen/folders/ZenFolder.mjs

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

* Update src/zen/workspaces/ZenWorkspaces.mjs

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

* fix: Set icon for new workspace

* fix: Formatting

* fix: Hide the icon if empty

* fix: Optimize tab drag-over transitions

* feat: Lower the drag and drop threshold, b=no-bug, c=tabs, folders

* feat: Hide search panel when theres no visible tabs, b=no-bug, c=tabs, folders

* fix: Adapt tab-group to new changes

* fix: Fixed expanding split views as folders, b=no-bug, c=folders

* feat(tabs): Implement tab grouping persistence for pinned tabs

This commit introduces the ability to group pinned tabs for better organization.

Changes include:

- Added `createGroup` to create new tab groups.
- Added `addTabToGroup` to add existing tabs to a group.
- Added `removeTabFromGroup` to remove a tab from a group (moving it to the root level).
- Added `moveTabBetweenGroups` to move tabs between different groups or to the root level.
- Added `getAllGroups` to retrieve all tab groups, optionally filtered by workspace.
- Added `getGroupInfo` to retrieve information about a specific group, including its child count.
- Added `reorderTabsInGroup` to reorder tabs within a specific group.

These functions provide a comprehensive API for managing tab groups within the Zen Browser.  Error handling and input validation are included for robustness.  Database transactions are used to ensure data consistency. Observers are notified of changes to notify the sync engine.

* feat: Improve stroke colors for light mode, b=no-bug, c=folders

* perf: cache and optimize animation updates

* fix: Expand folder after drop

* fix: Update active state on tab change

* feat: Sync groups to new windows, b=no-bug, c=folders, tabs, workspaces

* feat: Finish window syncing for new folders, b=no-bug, c=tabs, folders, workspaces

* feat: Make sure SVG icons use the context fill instead of the current color, b=no-bug, c=common, folders

* feat: Added support for drag and drop in collapsed mode, b=no-bug, c=tabs, folders

* fix: Clean up tab attributes and styles on workspace transfer

* fix: Fixed svg icons being always dark, b=no-bug, c=workspaces

---------

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Co-authored-by: Mr. M <mr.m@tuta.com>
Co-authored-by: Patrik Egyed <pregnor@gmail.com>
Co-authored-by: Kristijan Ribarić <kriba13@gmail.com>
This commit is contained in:
octaviusz
2025-08-06 18:59:08 +03:00
committed by GitHub
parent 7cf96dde23
commit 591dce5921
94 changed files with 3534 additions and 531 deletions

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e2ccc453a 100644
index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..1b934a6837a79e378d729462b62de424b0741bbd 100644
--- a/browser/components/tabbrowser/content/tabs.js
+++ b/browser/components/tabbrowser/content/tabs.js
@@ -289,6 +289,7 @@
@@ -28,16 +28,23 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let tabsPerRow = 0;
let position = RTL_UI
? window.windowUtils.getBoundsWithoutFlushing(
@@ -780,7 +781,7 @@
} else if (isTabGroupLabel(tab) && !tab.group.collapsed) {
@@ -777,11 +778,12 @@
if (tab.multiselected) {
this.#moveTogetherSelectedTabs(tab);
- } else if (isTabGroupLabel(tab) && !tab.group.collapsed) {
+ } else if (isTabGroupLabel(tab)) {
this._lockTabSizing();
this.#keepTabSizeLocked = true;
- tab.group.collapsed = true;
- expandGroupOnDrop = true;
+ tab.group.collapsed = !tab.group.hasAttribute("split-view-group");
expandGroupOnDrop = true;
+ expandGroupOnDrop = !tab.group.collapsed || tab.group.hasAttribute("has-active");
+ gZenFolders.collapseVisibleTab(tab.group);
}
}
@@ -879,7 +880,7 @@
@@ -879,7 +881,7 @@
? event.screenY - window.screenY - tabOffset
: event.screenY - window.screenY,
scrollPos:
@@ -46,7 +53,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
? this.pinnedTabsContainer.scrollPosition
: this.arrowScrollbox.scrollPosition,
screenX: event.screenX,
@@ -933,6 +934,10 @@
@@ -933,6 +935,10 @@
}
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
@@ -57,7 +64,18 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
if (
(effects == "move" || effects == "copy") &&
document == draggedTab.ownerDocument &&
@@ -1089,6 +1094,18 @@
@@ -1060,7 +1066,9 @@
isTabGroupLabel(draggedTab) &&
draggedTab._dragData?.expandGroupOnDrop
) {
- draggedTab.group.collapsed = false;
+ const isActive = draggedTab.group.hasAttribute("has-active");
+ draggedTab.group.collapsed = isActive;
+ if (isActive) gZenFolders.expandVisibleTab(draggedTab.group);
this.#keepTabSizeLocked = false;
this._unlockTabSizing();
}
@@ -1089,6 +1097,18 @@
this._tabDropIndicator.hidden = true;
event.stopPropagation();
@@ -76,11 +94,13 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
if (draggedTab && dropEffect == "copy") {
let duplicatedDraggedTab;
let duplicatedTabs = [];
@@ -1128,10 +1145,11 @@
@@ -1127,11 +1147,12 @@
newTranslateY -= tabHeight;
}
} else {
let isPinned = draggedTab.pinned;
- let isPinned = draggedTab.pinned;
- let numPinned = gBrowser.pinnedTabCount;
+ let isPinned = draggedTab?.group ? draggedTab.group.pinned : draggedTab.pinned;
+ let numPinned = gBrowser._numVisiblePinTabsWithoutCollapsed;
+ let essential = draggedTab.hasAttribute("zen-essential");
let tabs = this.ariaFocusableItems.slice(
@@ -91,16 +111,15 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
);
let size = this.verticalMode ? "height" : "width";
let screenAxis = this.verticalMode ? "screenY" : "screenX";
@@ -1180,7 +1198,7 @@
(oldTranslateX && oldTranslateX != newTranslateX) ||
@@ -1181,6 +1202,7 @@
(oldTranslateY && oldTranslateY != newTranslateY);
} else if (this.verticalMode) {
- shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY;
+ shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY && movingTabs.length === 1;
shouldTranslate &&= oldTranslateY && oldTranslateY != newTranslateY;
+ shouldTranslate = false; // TODO: Find a way to animate vertical tab moves.
} else {
shouldTranslate &&= oldTranslateX && oldTranslateX != newTranslateX;
}
@@ -1349,6 +1367,7 @@
@@ -1349,6 +1371,7 @@
let nextItem = this.ariaFocusableItems[newIndex];
let tabGroup = isTab(nextItem) && nextItem.group;
@@ -108,7 +127,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
gBrowser.loadTabs(urls, {
inBackground,
replace,
@@ -1381,6 +1400,17 @@
@@ -1381,6 +1404,17 @@
this.finishMoveTogetherSelectedTabs(draggedTab);
this.finishAnimateTabMove();
@@ -126,7 +145,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
this.#expandGroupOnDrop(draggedTab);
if (
@@ -1607,7 +1637,7 @@
@@ -1607,7 +1641,7 @@
}
get newTabButton() {
@@ -135,7 +154,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
get verticalMode() {
@@ -1623,6 +1653,7 @@
@@ -1623,6 +1657,7 @@
}
get overflowing() {
@@ -143,7 +162,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return this.hasAttribute("overflow");
}
@@ -1631,26 +1662,54 @@
@@ -1631,26 +1666,54 @@
if (this.#allTabs) {
return this.#allTabs;
}
@@ -205,7 +224,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
/**
@@ -1717,20 +1776,17 @@
@@ -1717,33 +1780,27 @@
let elementIndex = 0;
@@ -225,11 +244,16 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
child.labelElement.elementIndex = elementIndex++;
focusableItems.push(child.labelElement);
- if (!child.collapsed) {
+ if (!child.collapsed && !child.hasAttribute("split-view-group")) {
let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
visibleTabsInGroup.forEach(tab => {
- let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
- visibleTabsInGroup.forEach(tab => {
+ if (!child.hasAttribute("split-view-group")) {
+ let visibleTabsAndGroupsInGroup = child.childGroupsAndTabs.filter(tab => tab.visible);
+ visibleTabsAndGroupsInGroup.forEach(tab => {
tab.elementIndex = elementIndex++;
@@ -1740,10 +1796,7 @@
});
- focusableItems.push(...visibleTabsInGroup);
+ focusableItems.push(...visibleTabsAndGroupsInGroup);
}
}
}
@@ -241,7 +265,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return this.#focusableItems;
}
@@ -1751,6 +1804,7 @@
@@ -1751,6 +1808,7 @@
_invalidateCachedTabs() {
this.#allTabs = null;
this._invalidateCachedVisibleTabs();
@@ -249,7 +273,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
_invalidateCachedVisibleTabs() {
@@ -1766,8 +1820,8 @@
@@ -1766,8 +1824,8 @@
#isContainerVerticalPinnedGrid(tab) {
return (
this.verticalMode &&
@@ -260,7 +284,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
!this.expandOnHover
);
}
@@ -1783,7 +1837,7 @@
@@ -1783,7 +1841,7 @@
if (node == null) {
// We have a container for non-tab elements at the end of the scrollbox.
@@ -269,7 +293,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
node.before(tab);
@@ -1878,7 +1932,7 @@
@@ -1878,7 +1936,7 @@
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
// Attach the long click popup to all of them.
@@ -278,7 +302,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
const newTab2 = this.newTabButton;
const newTabVertical = document.getElementById(
"vertical-tabs-newtab-button"
@@ -1973,10 +2027,12 @@
@@ -1973,10 +2031,12 @@
_handleTabSelect(aInstant) {
let selectedTab = this.selectedItem;
@@ -291,7 +315,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
selectedTab._notselectedsinceload = false;
}
@@ -2130,7 +2186,7 @@
@@ -2130,7 +2190,7 @@
return;
}
@@ -300,7 +324,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let directionX = screenX > dragData.animLastScreenX;
let directionY = screenY > dragData.animLastScreenY;
@@ -2139,6 +2195,8 @@
@@ -2139,6 +2199,8 @@
let { width: tabWidth, height: tabHeight } =
draggedTab.getBoundingClientRect();
@@ -309,7 +333,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let shiftSizeX = tabWidth * movingTabs.length;
let shiftSizeY = tabHeight;
dragData.tabWidth = tabWidth;
@@ -2168,7 +2226,7 @@
@@ -2168,7 +2230,7 @@
let translateX = screenX - dragData.screenX;
let translateY = screenY - dragData.screenY;
translateY +=
@@ -318,7 +342,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let firstBoundX = firstTabInRow.screenX - firstMovingTabScreenX;
let firstBoundY = firstTabInRow.screenY - firstMovingTabScreenY;
let lastBoundX =
@@ -2294,7 +2352,7 @@
@@ -2294,7 +2356,7 @@
}
dragData.animDropElementIndex = newIndex;
@@ -327,7 +351,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
dragData.dropBefore = newIndex < tabs.length;
// Shift background tabs to leave a gap where the dragged tab
@@ -2327,12 +2385,16 @@
@@ -2327,12 +2389,16 @@
this.#clearDragOverCreateGroupTimer();
@@ -348,7 +372,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
if (this.#rtlMode) {
tabs.reverse();
@@ -2346,7 +2408,7 @@
@@ -2346,7 +2412,7 @@
let size = this.verticalMode ? "height" : "width";
let translateAxis = this.verticalMode ? "translateY" : "translateX";
let scrollDirection = this.verticalMode ? "scrollTop" : "scrollLeft";
@@ -357,7 +381,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let translateX = event.screenX - dragData.screenX;
let translateY = event.screenY - dragData.screenY;
@@ -2360,12 +2422,21 @@
@@ -2360,12 +2426,18 @@
let lastTab = tabs.at(-1);
let lastMovingTab = movingTabs.at(-1);
let firstMovingTab = movingTabs[0];
@@ -371,16 +395,13 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
let lastMovingTabScreen = endEdge(lastMovingTab);
let firstMovingTabScreen = firstMovingTab[screenAxis];
let shiftSize = lastMovingTabScreen - firstMovingTabScreen;
+ if (firstMovingTab.hasAttribute("split-view-group")) {
+ shiftSize += 5; // A hack to allow more space for the group
+ }
let translate = screen - dragData[screenAxis];
- if (!isPinned) {
+ if (true) {
translate +=
this.arrowScrollbox.scrollbox[scrollDirection] - dragData.scrollPos;
} else if (isPinned && this.verticalMode) {
@@ -2384,6 +2455,9 @@
@@ -2384,6 +2456,9 @@
// Shift the `.tab-group-label-container` to shift the label element.
item = item.parentElement;
}
@@ -390,17 +411,25 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
item.style.transform = `${translateAxis}(${translate}px)`;
}
@@ -2521,6 +2595,9 @@
@@ -2521,6 +2596,9 @@
break;
}
let element = tabs[mid];
+ if (element?.group?.hasAttribute("split-view-group")) {
+ element = element.group.labelElement;
+ element = element.group;
+ }
let elementForSize = isTabGroupLabel(element)
? element.parentElement
: element;
@@ -2604,7 +2681,7 @@
@@ -2540,6 +2618,7 @@
};
let dropElement = getOverlappedElement();
+ if (dropElement?.hasAttribute("split-view-group")) dropElement = dropElement.labelElement;
let newDropElementIndex;
if (dropElement) {
@@ -2604,7 +2683,7 @@
let shouldCreateGroupOnDrop;
let dropBefore;
if (dropElement) {
@@ -409,16 +438,40 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
? dropElement.parentElement
: dropElement;
@@ -2659,7 +2736,7 @@
if (
isTabGroupLabel(draggedTab) &&
dropElement?.group &&
- !dropElement.group.collapsed
+ !dropElement.group.collapsed && !dropElement.group.hasAttribute("split-view-group")
) {
let overlappedGroup = dropElement.group;
@@ -2624,7 +2703,7 @@
? Services.prefs.getIntPref(
"browser.tabs.dragDrop.moveOverThresholdPercent"
) / 100
- : 0.5;
+ : Services.prefs.getIntPref('zen.view.drag-and-drop.move-over-threshold') / 100;
moveOverThreshold = Math.min(1, Math.max(0, moveOverThreshold));
let shouldMoveOver = overlapPercent > moveOverThreshold;
if (logicalForward && shouldMoveOver) {
@@ -2656,23 +2735,6 @@
@@ -2686,12 +2763,7 @@
// If dragging a group over another group, don't make it look like it is
// possible to drop the dragged group inside the other group.
- if (
- isTabGroupLabel(draggedTab) &&
- dropElement?.group &&
- !dropElement.group.collapsed
- ) {
- let overlappedGroup = dropElement.group;
-
- if (isTabGroupLabel(dropElement)) {
- dropBefore = true;
- newDropElementIndex = dropElement.elementIndex;
- } else {
- dropBefore = false;
- newDropElementIndex = overlappedGroup.tabs.at(-1).elementIndex + 1;
- }
-
- dropElement = overlappedGroup;
- }
// Constrain drop direction at the boundary between pinned and
// unpinned tabs so that they don't mix together.
@@ -2686,14 +2748,13 @@
}
}
@@ -428,20 +481,64 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
- !isPinned &&
- (!numPinned || newDropElementIndex > numPinned)
- ) {
+ if (isTab(draggedTab)) {
+ if (isTab(draggedTab) || isTabGroupLabel(draggedTab)) {
let dragOverGroupingThreshold = 1 - moveOverThreshold;
+ if (draggedTab && !dropElement?.group) {
+ gZenFolders.highlightGroupOnDragOver(null);
+ }
+
// When dragging tab(s) over an ungrouped tab, signal to the user
@@ -2739,7 +2811,7 @@
// Dropping right before the tab group.
dropElement = dropElementGroup;
colorCode = undefined;
// that dropping the tab(s) will create a new tab group.
shouldCreateGroupOnDrop =
@@ -2703,12 +2764,12 @@
overlapPercent > dragOverGroupingThreshold;
if (shouldCreateGroupOnDrop) {
- this.#dragOverCreateGroupTimer = setTimeout(
- () => this.#triggerDragOverCreateGroup(dragData, dropElement),
- Services.prefs.getIntPref(
- "browser.tabs.dragDrop.createGroup.delayMS"
- )
- );
+ // this.#dragOverCreateGroupTimer = setTimeout(
+ // () => this.#triggerDragOverCreateGroup(dragData, dropElement),
+ // Services.prefs.getIntPref(
+ // "browser.tabs.dragDrop.createGroup.delayMS"
+ // )
+ // );
} else {
this.removeAttribute("movingtab-createGroup");
document
@@ -2735,19 +2796,14 @@
dropElement = dropElementGroup;
colorCode = undefined;
} else if (isTabGroupLabel(dropElement)) {
- if (dropBefore) {
- // Dropping right before the tab group.
- dropElement = dropElementGroup;
- colorCode = undefined;
- } else if (dropElementGroup.collapsed) {
+ } else if (dropElement?.group?.hasAttribute("split-view-group")) {
// Dropping right after the collapsed tab group.
dropElement = dropElementGroup;
colorCode = undefined;
@@ -2769,7 +2841,7 @@
- // Dropping right after the collapsed tab group.
- dropElement = dropElementGroup;
- colorCode = undefined;
- } else {
- // Dropping right before the first tab in the tab group.
- dropElement = dropElementGroup.tabs[0];
- dropBefore = true;
- }
+ ({ dropElement, colorCode, dropBefore } = gZenFolders.handleDragOverTabGroupLabel(
+ dropElement,
+ draggedTab,
+ overlapPercent,
+ movingTabs,
+ dropBefore,
+ colorCode
+ ));
}
this.#setDragOverGroupColor(colorCode);
this.toggleAttribute("movingtab-ungroup", !colorCode);
@@ -2769,15 +2825,24 @@
// Shift background tabs to leave a gap where the dragged tab
// would currently be dropped.
for (let item of tabs) {
@@ -450,7 +547,14 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
continue;
}
@@ -2778,6 +2850,9 @@
let shift = getTabShift(item, newDropElementIndex);
let transform = shift ? `${translateAxis}(${shift}px)` : "";
+ if (item.group?.hasAttribute("split-view-group")) {
+ item = item.group;
+ }
+ if (item.group?.hasAttribute("has-active") && draggedTab.group != item.group) {
+ item = item.group;
+ }
if (isTabGroupLabel(item)) {
// Shift the `.tab-group-label-container` to shift the label element.
item = item.parentElement;
@@ -460,7 +564,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
}
item.style.transform = transform;
}
@@ -2830,8 +2905,9 @@
@@ -2830,12 +2895,14 @@
);
}
@@ -472,10 +576,21 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return;
}
@@ -2843,6 +2919,12 @@
this.#setMovingTabMode(false);
+ gZenFolders.highlightGroupOnDragOver(null);
for (let item of this.ariaFocusableItems) {
if (isTabGroupLabel(item)) {
@@ -2843,6 +2910,18 @@
item = item.parentElement;
}
item.style.transform = "";
+ if (item.closest("zen-folder")?.hasAttribute("has-active")) item.closest("zen-folder").style.transform = "";
+ if (item.closest("zen-folder")?.hasAttribute("has-active")) {
+ for (let tab of item.closest("zen-folder").tabs) {
+ tab.style.transform = "";
+ }
+ }
+ if (item.closest("tab-group")?.hasAttribute("split-view-group")) item.closest("tab-group").style.transform = "";
+ if (item.closest("tab-group")?.hasAttribute("split-view-group")) {
+ for (let tab of item.closest("tab-group").tabs) {
@@ -485,7 +600,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
item.removeAttribute("dragover-createGroup");
}
this.removeAttribute("movingtab-createGroup");
@@ -2889,7 +2971,7 @@
@@ -2889,7 +2968,7 @@
let postTransitionCleanup = () => {
movingTab._moveTogetherSelectedTabsData.animate = false;
};
@@ -494,7 +609,7 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
@@ -3062,7 +3144,7 @@
@@ -3062,7 +3141,7 @@
}
_notifyBackgroundTab(aTab) {
@@ -503,6 +618,18 @@ index 1fcebe3962398ff1b7cadef657ac8b68a80e720d..d3c0a1ac7f24301e56cb46e83a4d9b8e
return;
}
@@ -3171,7 +3250,10 @@
#getDragTarget(event, { ignoreSides = false } = {}) {
let { target } = event;
while (target) {
- if (isTab(target) || isTabGroupLabel(target)) {
+ if (isTab(target) || isTabGroupLabel(target) || target?.classList?.contains("tab-group-label-container")) {
+ if (target.classList?.contains("tab-group-label-container")) {
+ target = target.querySelector(".tab-group-label");
+ }
break;
}
target = target.parentNode;
@@ -3188,6 +3270,9 @@
return null;
}