From 86390bde0241a1b4b38f3ccbcade4804bdc52e34 Mon Sep 17 00:00:00 2001 From: "mr. M" Date: Sat, 1 Mar 2025 19:19:22 +0100 Subject: [PATCH] Enhance split view functionality and styling improvements --- .../base/content/zen-styles/zen-decks.css | 1 + .../base/content/zen-styles/zen-folders.css | 26 +++++- .../zen-styles/zen-tabs/vertical-tabs.css | 4 +- .../base/zen-components/ZenViewSplitter.mjs | 3 +- .../base/zen-components/ZenWorkspaces.mjs | 32 ++++---- .../tabbrowser/content/tabbrowser-js.patch | 80 +++++++++++-------- 6 files changed, 92 insertions(+), 54 deletions(-) diff --git a/src/browser/base/content/zen-styles/zen-decks.css b/src/browser/base/content/zen-styles/zen-decks.css index ba7a19093..1d087082a 100644 --- a/src/browser/base/content/zen-styles/zen-decks.css +++ b/src/browser/base/content/zen-styles/zen-decks.css @@ -34,6 +34,7 @@ margin-left: 0 !important; position: absolute !important; overflow: hidden; + transition: inset 0.1s; } #tabbrowser-tabpanels[zen-split-view='true']:not([zen-split-resizing]) > [zen-split-anim='true'] { diff --git a/src/browser/base/content/zen-styles/zen-folders.css b/src/browser/base/content/zen-styles/zen-folders.css index 06c516840..2f8df029f 100644 --- a/src/browser/base/content/zen-styles/zen-folders.css +++ b/src/browser/base/content/zen-styles/zen-folders.css @@ -13,14 +13,21 @@ tab-group[split-view-group] { align-items: center; --zen-split-view-active-tab-bg: color-mix(in srgb, var(--zen-toolbar-element-bg), transparent 40%); + :root:not([zen-sidebar-expanded='true']) & { + padding: 2px 0; + } + & > .tabbrowser-tab { - --tab-min-height: 28px; --tab-selected-bgcolor: var(--zen-split-view-active-tab-bg); --tab-hover-background-color: transparent; --tab-selected-shadow: none; --border-radius-medium: var(--tab-border-radius); --zen-active-tab-scale: 1; + :root[zen-sidebar-expanded='true'] & { + --tab-min-height: 28px; + } + flex: 1 !important; &:not(:last-child)::after { @@ -33,6 +40,15 @@ tab-group[split-view-group] { top: 50%; transform: translateY(-50%); } + + :root:not([zen-sidebar-expanded='true']) &:not(:last-child)::after { + width: 16px; + height: 1px; + top: auto; + bottom: 0; + right: 50%; + transform: translateX(50%); + } } &:has(> tab[visuallyselected]) { @@ -42,7 +58,7 @@ tab-group[split-view-group] { & > .tabbrowser-tab { --tab-hover-background-color: var(--zen-split-view-active-tab-bg); & .tab-background { - background-color: var(--zen-split-view-active-tab-bg); + background-color: var(--zen-split-view-active-tab-bg) !important; } &::after { @@ -70,6 +86,12 @@ tab-group[split-view-group] { } } +:root:not([zen-sidebar-expanded='true']) { + tab-group { + flex-direction: column; + } +} + tab-group[split-view-group] .tabbrowser-tab { width: 100%; max-width: unset; diff --git a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css index 9c72d80ae..0098d1d03 100644 --- a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css +++ b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css @@ -391,7 +391,7 @@ max-height: unset !important; & .tabbrowser-tab:not(:hover) .tab-background:not([selected]):not([multiselected]) { - background: transparent !important; + background: transparent; } & .tabbrowser-tab .tab-content { @@ -872,7 +872,7 @@ display: block; width: 2.5px; height: 16px; - background: light-dark(rgba(88, 79, 79, 0.02), rgba(255, 255, 255, 0.3)); + background: light-dark(rgba(66, 61, 61, 0.3), rgba(255, 255, 255, 0.3)); position: absolute; right: 0; top: 50%; diff --git a/src/browser/base/zen-components/ZenViewSplitter.mjs b/src/browser/base/zen-components/ZenViewSplitter.mjs index d9577d093..6e3f5ec16 100644 --- a/src/browser/base/zen-components/ZenViewSplitter.mjs +++ b/src/browser/base/zen-components/ZenViewSplitter.mjs @@ -1383,9 +1383,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature { label: '', showCreateUI: false, insertBefore: tabs[0], + forSplitView: true, }); - - group.setAttribute('split-view-group', true); } return null; diff --git a/src/browser/base/zen-components/ZenWorkspaces.mjs b/src/browser/base/zen-components/ZenWorkspaces.mjs index f4546e81a..769e11964 100644 --- a/src/browser/base/zen-components/ZenWorkspaces.mjs +++ b/src/browser/base/zen-components/ZenWorkspaces.mjs @@ -263,6 +263,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { _organizeTabsToWorkspaceSections(workspace, section, pinnedSection, tabs) { const workspaceTabs = Array.from(tabs).filter((tab) => tab.getAttribute('zen-workspace-id') === workspace.uuid); + let firstNormalTab = null; for (const tab of workspaceTabs) { if (tab.hasAttribute('zen-essential')) { continue; // Ignore essentials as they need to be in their own section @@ -272,9 +273,18 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { if (tab.pinned) { pinnedSection.insertBefore(tab, pinnedSection.nextSibling); } else { + if (!firstNormalTab) { + firstNormalTab = tab; + } section.insertBefore(tab, section.lastChild); } } + // Kind of a hacky fix, but for some reason the first normal tab in the list + // created by session restore is added the the last position of the tab list + // let's just prepend it to the section + if (firstNormalTab) { + section.insertBefore(firstNormalTab, section.firstChild); + } } initializeWorkspaceNavigation() { @@ -989,21 +999,15 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { }.bind(browser.ZenWorkspaces) ); - element.addEventListener( - 'dragenter', - function (event) { - if (this.isReorderModeOn(browser) && this.draggedElement) { - element.classList.add('dragover'); - } - }.bind(browser.ZenWorkspaces) - ); + element.addEventListener('dragenter', function (event) { + if (this.isReorderModeOn(browser) && this.draggedElement) { + element.classList.add('dragover'); + } + }); - element.addEventListener( - 'dragleave', - function (event) { - element.classList.remove('dragover'); - }.bind(browser.ZenWorkspaces) - ); + element.addEventListener('dragleave', function (event) { + element.classList.remove('dragover'); + }); element.addEventListener( 'drop', diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 2f2f5c08f..77d7c2bcc 100644 --- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch +++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js -index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e94798f4f7 100644 +index 628aa6596627c85efe361fc1ece8fd58f7ee653e..f4a4ad413785e08bbb70df8cbb5feefd71c4dfe1 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -412,11 +412,50 @@ @@ -238,17 +238,28 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 // Additionally send pinned tab events if (pinned) { this._notifyPinnedStatus(t); -@@ -2918,7 +2990,8 @@ +@@ -2904,6 +2976,7 @@ + label = "", + insertBefore = null, + showCreateUI = false, ++ forSplitView = false, + } = {} + ) { + if (!tabs?.length) { +@@ -2918,7 +2991,11 @@ id = `${Date.now()}-${Math.round(Math.random() * 100)}`; } let group = this._createTabGroup(id, color, false, label); - this.tabContainer.insertBefore( ++ if (forSplitView) { ++ group.setAttribute('split-view-group', true); ++ } + group.pinned = tabs.some(tab => tab.pinned); + (group.pinned ? this.verticalPinnedTabsContainer : this.tabContainer).insertBefore( group, insertBefore?.group ?? insertBefore ); -@@ -3126,6 +3199,7 @@ +@@ -3126,6 +3203,7 @@ initialBrowsingContextGroupId, openWindowInfo, skipLoad, @@ -256,7 +267,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 } ) { // If we don't have a preferred remote type, and we have a remote -@@ -3189,6 +3263,7 @@ +@@ -3189,6 +3267,7 @@ openWindowInfo, name, skipLoad, @@ -264,7 +275,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 }); } -@@ -3367,6 +3442,24 @@ +@@ -3367,6 +3446,24 @@ ) { tabWasReused = true; tab = this.selectedTab; @@ -289,7 +300,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (!tabData.pinned) { this.unpinTab(tab); } else { -@@ -3380,6 +3473,7 @@ +@@ -3380,6 +3477,7 @@ restoreTabsLazily && !select && !tabData.pinned; let url = "about:blank"; @@ -297,7 +308,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (tabData.entries?.length) { let activeIndex = (tabData.index || tabData.entries.length) - 1; // Ensure the index is in bounds. -@@ -3415,7 +3509,24 @@ +@@ -3415,7 +3513,24 @@ skipLoad: true, preferredRemoteType, }); @@ -323,7 +334,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (select) { tabToSelect = tab; } -@@ -3428,8 +3539,8 @@ +@@ -3428,8 +3543,8 @@ // inserted in the DOM. If the tab is not yet in the DOM, // just insert it in the right place from the start. if (!tab.parentNode) { @@ -334,7 +345,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 tab.toggleAttribute("pinned", true); this.tabContainer._invalidateCachedTabs(); // Then ensure all the tab open/pinning information is sent. -@@ -3693,7 +3804,7 @@ +@@ -3693,7 +3808,7 @@ // Ensure we have an index if one was not provided. if (typeof index != "number") { // Move the new tab after another tab if needed, to the end otherwise. @@ -343,7 +354,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if ( !bulkOrderedOpen && ((openerTab && -@@ -3736,18 +3847,18 @@ +@@ -3736,18 +3851,18 @@ // Ensure index is within bounds. if (tab.pinned) { @@ -366,7 +377,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (tabAfter && tabAfter.group == tabGroup) { // Place at the front of, or between tabs in, the same tab group this.tabContainer.insertBefore(tab, tabAfter); -@@ -4059,6 +4170,9 @@ +@@ -4059,6 +4174,9 @@ return; } @@ -376,7 +387,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 this.removeTabs(selectedTabs); } -@@ -4391,6 +4505,7 @@ +@@ -4391,6 +4509,7 @@ skipSessionStore, } = {} ) { @@ -384,7 +395,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (UserInteraction.running("browser.tabs.opening", window)) { UserInteraction.finish("browser.tabs.opening", window); } -@@ -4407,6 +4522,12 @@ +@@ -4407,6 +4526,12 @@ TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab); } @@ -397,7 +408,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 // Handle requests for synchronously removing an already // asynchronously closing tab. if (!animate && aTab.closing) { -@@ -4421,7 +4542,9 @@ +@@ -4421,7 +4546,9 @@ // frame created for it (for example, by updating the visually selected // state). let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width; @@ -408,7 +419,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if ( !this._beginRemoveTab(aTab, { closeWindowFastpath: true, -@@ -4435,7 +4558,6 @@ +@@ -4435,7 +4562,6 @@ TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab); return; } @@ -416,7 +427,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 let lockTabSizing = !this.tabContainer.verticalMode && !aTab.pinned && -@@ -4574,14 +4696,14 @@ +@@ -4574,14 +4700,14 @@ !!this.tabsInCollapsedTabGroups.length; if ( aTab.visible && @@ -433,7 +444,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (closeWindow) { // We've already called beforeunload on all the relevant tabs if we get here, -@@ -4605,6 +4727,7 @@ +@@ -4605,6 +4731,7 @@ newTab = true; } @@ -441,7 +452,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 aTab._endRemoveArgs = [closeWindow, newTab]; // swapBrowsersAndCloseOther will take care of closing the window without animation. -@@ -4645,9 +4768,7 @@ +@@ -4645,9 +4772,7 @@ aTab._mouseleave(); if (newTab) { @@ -452,7 +463,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 } else { TabBarVisibility.update(); } -@@ -4776,6 +4897,8 @@ +@@ -4776,6 +4901,8 @@ this.tabs[i]._tPos = i; } @@ -461,7 +472,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (!this._windowIsClosing) { if (wasPinned) { this.tabContainer._positionPinnedTabs(); -@@ -4994,7 +5117,7 @@ +@@ -4994,7 +5121,7 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -470,7 +481,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 } // Try to find a remaining tab that comes after the given tab -@@ -5016,7 +5139,7 @@ +@@ -5016,7 +5143,7 @@ } if (tab) { @@ -479,7 +490,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 } // If no qualifying visible tab was found, see if there is a tab in -@@ -5434,10 +5557,10 @@ +@@ -5434,10 +5561,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -492,7 +503,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -5675,7 +5798,7 @@ +@@ -5675,7 +5802,7 @@ // Don't allow mixing pinned and unpinned tabs. if (aTab.pinned) { @@ -501,7 +512,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 } else { aIndex = Math.max(aIndex, this.pinnedTabCount); } -@@ -5684,11 +5807,17 @@ +@@ -5684,11 +5811,18 @@ } this._handleTabMove(aTab, () => { @@ -511,18 +522,19 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 + const _tPos = aTab._tPos; + if ((forceStandaloneTab && neighbor.group) || neighbor.group?.hasAttribute("split-view-group")) { neighbor = neighbor.group; -+ } else if (aTab.group && aTab.group.hasAttribute("split-view-group")) { + } +- if (neighbor && aIndex >= aTab._tPos) { ++ if (aTab.group?.hasAttribute("split-view-group")) { + aTab = aTab.group; + } + if (aTab.group?.hasAttribute("split-view-group") && neighbor == aTab.group) { + return; - } -- if (neighbor && aIndex >= aTab._tPos) { ++ } + if (neighbor && aIndex >= _tPos) { neighbor.after(aTab); } else { this.tabContainer.insertBefore(aTab, neighbor); -@@ -5697,7 +5826,7 @@ +@@ -5697,7 +5831,7 @@ } moveTabToGroup(aTab, aGroup) { @@ -531,7 +543,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 return; } if (aTab.group && aTab.group.id === aGroup.id) { -@@ -5721,6 +5850,8 @@ +@@ -5721,6 +5855,8 @@ moveActionCallback(); @@ -540,7 +552,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -5771,7 +5902,7 @@ +@@ -5771,7 +5907,7 @@ createLazyBrowser, }; @@ -549,7 +561,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) { params.pinned = true; } -@@ -7415,6 +7546,7 @@ +@@ -7415,6 +7551,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -557,7 +569,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -8381,7 +8513,7 @@ var TabContextMenu = { +@@ -8381,7 +8518,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !multiselectionContext; @@ -566,7 +578,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 // Move Tab items let contextMoveTabOptions = document.getElementById( "context_moveTabOptions" -@@ -8414,7 +8546,7 @@ var TabContextMenu = { +@@ -8414,7 +8551,7 @@ var TabContextMenu = { let contextMoveTabToStart = document.getElementById("context_moveToStart"); let isFirstTab = tabsToMove[0] == visibleTabs[0] || @@ -575,7 +587,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..287ff24e0fee10f435fd33914756d9e9 contextMoveTabToStart.disabled = isFirstTab && allSelectedTabsAdjacent; document.getElementById("context_openTabInWindow").disabled = -@@ -8647,6 +8779,7 @@ var TabContextMenu = { +@@ -8647,6 +8784,7 @@ var TabContextMenu = { if (this.contextTab.multiselected) { gBrowser.removeMultiSelectedTabs(); } else {