Started working on folders implementation and better split view integration

This commit is contained in:
mr. M
2025-02-26 23:51:38 +01:00
parent 65d56afc02
commit 18d62429bf
14 changed files with 562 additions and 104 deletions

View File

@@ -17,6 +17,7 @@
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-urlbar.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-workspaces.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-decks.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-folders.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-glance.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-sidebar-panels.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-popup.css" />
@@ -28,6 +29,7 @@
</linkset>
# Scripts used all over the browser
<script src="chrome://browser/content/zen-components/ZenFolders.mjs" />
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs" />
<script src="chrome://browser/content/zen-components/ZenThemesImporter.mjs" />
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs" />

View File

@@ -20,6 +20,7 @@
content/browser/zen-components/ZenCommonUtils.mjs (zen-components/ZenCommonUtils.mjs)
content/browser/zen-components/ZenGradientGenerator.mjs (zen-components/ZenGradientGenerator.mjs)
content/browser/zen-components/ZenGlanceManager.mjs (zen-components/ZenGlanceManager.mjs)
content/browser/zen-components/ZenFolders.mjs (zen-components/ZenFolders.mjs)
content/browser/zen-components/ZenActorsManager.mjs (zen-components/ZenActorsManager.mjs)
content/browser/zen-components/ZenRices.mjs (zen-components/ZenRices.mjs)
content/browser/zen-components/ZenEmojies.mjs (zen-components/ZenEmojies.mjs)
@@ -37,6 +38,7 @@
content/browser/zen-styles/zen-sidebar.css (content/zen-styles/zen-sidebar.css)
content/browser/zen-styles/zen-toolbar.css (content/zen-styles/zen-toolbar.css)
content/browser/zen-styles/zen-decks.css (content/zen-styles/zen-decks.css)
content/browser/zen-styles/zen-folders.css (content/zen-styles/zen-folders.css)
content/browser/zen-styles/zen-glance.css (content/zen-styles/zen-glance.css)
content/browser/zen-styles/zen-browser-container.css (content/zen-styles/zen-browser-container.css)
content/browser/zen-styles/zen-workspaces.css (content/zen-styles/zen-workspaces.css)

View File

@@ -85,18 +85,6 @@
}
}
@keyframes zen-deck-fadeIn {
0% {
transform: scale(0.9);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes better-sidebar-pinned-hide {
0% {
opacity: 1;

View File

@@ -36,10 +36,6 @@
overflow: hidden;
}
#tabbrowser-tabpanels[zen-split-view='true'] > [zen-split-anim='true'] {
animation: zen-deck-fadeIn 0.2s forwards ease-in-out;
}
#tabbrowser-tabpanels[zen-split-view='true']:not([zen-split-resizing]) > [zen-split-anim='true'] {
transition: inset 0.09s ease-out !important;
& browser {
@@ -216,3 +212,46 @@
#zenSplitViewModifier:not([has-enabled-realloc])[toast] #zenSplitViewModifierEnabledToast {
display: revert;
}
.zen-view-splitter-header {
display: flex;
width: 100%;
height: 20px;
/*TODO: update with appropriate color*/
background-color: var(--zen-colors-secondary);
align-items: center;
z-index: 100;
}
.zen-view-splitter-drag-handle {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
cursor: move;
}
.zen-tab-unsplit-button {
display: block;
-moz-context-properties: fill, fill-opacity;
border-radius: var(--tab-border-radius);
/*TODO: update with appropriate color*/
color: inherit;
fill: currentColor;
padding: var(--tab-close-button-padding);
width: 24px;
height: 24px;
cursor: pointer;
appearance: none;
outline: var(--toolbarbutton-outline);
list-style-image: url('chrome://global/skin/icons/close-fill.svg') !important;
&:hover {
outline-color: var(--toolbarbutton-hover-outline-color);
}
&:hover:active {
outline-color: var(--toolbarbutton-active-outline-color);
}
}

View File

@@ -0,0 +1,108 @@
tab-group[split-view-group] {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(calc(var(--tab-pinned-min-width-expanded) + 20px), auto));
gap: 1px;
border-radius: var(--border-radius-medium);
padding: 0;
margin-inline: var(--tab-block-margin);
margin-block: var(--tab-block-margin);
min-height: var(--tab-min-height);
outline: var(--tab-outline);
outline-offset: var(--tab-outline-offset);
outline-color: var(--tab-selected-outline-color);
padding: 1px;
transition: scale 0.1s ease;
& > * {
--tab-min-height: 32px;
--tab-selected-bgcolor: var(--zen-toolbar-element-bg);
--tab-selected-shadow: none;
--border-radius-medium: calc(2px + var(--tab-border-radius));
--zen-active-tab-scale: 1;
}
&:has(tab[visuallyselected]) {
background-color: var(--tab-selected-bgcolor);
box-shadow: var(--tab-selected-shadow);
}
&:active {
scale: var(--zen-active-tab-scale);
}
&:hover {
background-color: var(--zen-toolbar-element-bg);
}
& .tab-close-button {
display: none !important;
}
}
tab-group[split-view-group] .tabbrowser-tab {
width: 100%;
max-width: unset;
}
tab-group[split-view-group] .tab-group-label-container {
display: none;
width: 100%;
}
tab-group[split-view-group] .tab-close-button {
display: block;
visibility: visible;
}
tab-group[split-view-group] .tab-group-line {
display: none;
background: transparent;
}
tab-group:not([split-view-group]) {
& .tab-group-label-container {
min-width: fit-content;
max-width: 100%;
height: fit-content !important;
display: flex;
justify-content: start;
margin-top: 10px;
margin-bottom: 10px;
}
& .tab-group-label {
text-align: start;
flex-grow: 1 !important;
min-width: fit-content;
max-width: 100%;
font-size: 14px !important;
display: block !important;
padding-right: 8px;
}
& .tab-group-line {
display: none !important;
}
&[collapsed] .tabbrowser-tab {
display: none !important;
}
&:not([collapsed]) .tabbrowser-tab {
margin-left: 10px;
}
&:not([collapsed]) .tabbrowser-tab::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 2px;
height: 100%;
background-color: var(--tab-group-color);
}
}
.tab-group-line {
display: none !important;
}

View File

@@ -265,7 +265,7 @@
}
&:active {
scale: 0.98;
scale: var(--zen-active-tab-scale);
}
max-width: unset;

View File

@@ -172,6 +172,7 @@
--arrowpanel-background: var(--zen-dialog-background) !important;
--zen-big-shadow: 0 0 9.73px 0px light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.25));
--zen-active-tab-scale: 0.98;
/* Nativity */
--zen-native-content-radius: var(--zen-border-radius);

View File

@@ -0,0 +1,28 @@
{
class ZenFolders {
constructor() {
this.#initEventListeners();
}
#initEventListeners() {
document.addEventListener('TabGrouped', this.#onTabGrouped.bind(this));
document.addEventListener('TabUngrouped', this.#onTabUngrouped.bind(this));
document.addEventListener('TabGroupRemoved', this.#onTabGroupRemoved.bind(this));
}
#onTabGrouped(event) {
const tab = event.target;
const group = tab.group;
group.pinned = tab.pinned;
}
#onTabUngrouped(event) {
}
#onTabGroupRemoved(event) {
}
}
window.gZenFolders = new ZenFolders();
}

View File

@@ -840,7 +840,8 @@
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');
let targetTab = event.target.closest('.tabbrowser-tab');
targetTab = targetTab?.group || targetTab;
if (event.target.closest('.zen-current-workspace-indicator')) {
this.removeTabContainersDragoverClass();
ZenWorkspaces.activeWorkspaceIndicator.setAttribute('open', true);

View File

@@ -120,8 +120,18 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
group.tabs.splice(tabIndex, 1);
this.resetTabState(tab, forUnsplit);
if (tab.group && tab.group.hasAttribute('split-view-group')) {
gBrowser.ungroupTab(tab);
}
if (group.tabs.length < 2) {
// We need to remove all remaining tabs from the group when unsplitting
let remainingTabs = [...group.tabs]; // Copy array since we'll modify it
for (let remainingTab of remainingTabs) {
if (remainingTab.group && remainingTab.group.hasAttribute('split-view-group')) {
gBrowser.ungroupTab(remainingTab);
}
this.resetTabState(remainingTab, forUnsplit);
}
this.removeGroup(groupIndex);
} else {
const node = this.getSplitNodeFromTab(tab);
@@ -192,13 +202,14 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
afterRearangeAction() {
document.getElementById('zenSplitViewModifier').hidePopup();
gZenUIManager.showToast('zen-split-view-modifier-enabled-toast', {
ConfirmationHint.show(document.getElementById('zen-split-views-box'), 'zen-split-view-modifier-enabled-toast', {
descriptionId: 'zen-split-view-modifier-enabled-toast-description',
showDescription: true,
});
}
afterRearangeRemove() {
gZenUIManager.showToast('zen-split-view-modifier-disabled-toast');
ConfirmationHint.show(document.getElementById('zen-split-views-box'), 'zen-split-view-modifier-disabled-toast');
}
toggleWrapperDisplay(value) {
@@ -208,7 +219,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
wrapper.setAttribute('hidden', !value);
}
enableTabRearrangeView() {
enableTabRearrangeView(tabDrag = false) {
if (this.rearrangeViewEnabled) return;
this.rearrangeViewEnabled = true;
this.rearrangeViewView = this.currentView;
@@ -223,13 +234,19 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
b.style.pointerEvents = 'none';
b.style.opacity = '.85';
});
this.tabBrowserPanel.addEventListener('dragstart', this.onBrowserDragStart);
if (!tabDrag) {
this.tabBrowserPanel.addEventListener('dragstart', this.onBrowserDragStart);
this.tabBrowserPanel.addEventListener('dragend', this.onBrowserDragEnd);
}
this.tabBrowserPanel.addEventListener('dragover', this.onBrowserDragOver);
this.tabBrowserPanel.addEventListener('drop', this.onBrowserDrop);
this.tabBrowserPanel.addEventListener('dragend', this.onBrowserDragEnd);
this.tabBrowserPanel.addEventListener('click', this.disableTabRearrangeView);
window.addEventListener('keydown', this.disableTabRearrangeView);
this.afterRearangeAction();
if (!tabDrag) {
this.afterRearangeAction();
}
}
disableTabRearrangeView = (event = null) => {
@@ -257,21 +274,57 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
});
this.rearrangeViewEnabled = false;
this.rearrangeViewView = null;
this.afterRearangeRemove();
if (!event?.type === 'dragend') {
// Don't show toast if exiting from drag
this.afterRearangeRemove();
}
};
onBrowserDragStart = (event) => {
if (!this.splitViewActive) return;
let browser = event.target.querySelector('browser');
if (!browser) {
return;
}
browser.style.opacity = '.2';
const browserContainer = browser.closest('.browserSidebarContainer');
event.dataTransfer.setData('text/plain', browserContainer.id);
this._draggingTab = gBrowser.getTabForBrowser(browser);
let dt = event.dataTransfer;
let browser;
let isSplitHeaderDrag = false;
const container = event.target.closest('.browserSidebarContainer[zen-split]');
if (container && event.offsetY < 20) {
// Split tab header drag case
const containerRect = container.getBoundingClientRect();
const clickX = event.clientX - containerRect.left;
// Only allow drag if click is NOT in right 20px (close button area)
if (clickX > containerRect.width - 22) {
return;
}
browser = container.querySelector('browser');
isSplitHeaderDrag = true;
} else {
// Regular browser drag case
browser = event.target.querySelector('browser');
}
if (!browser) return;
const tab = gBrowser.getTabForBrowser(browser);
if (!tab) return;
// Store the necessary state for drag end
this._dragState = {
tab,
browser,
isSplitHeaderDrag,
};
if (isSplitHeaderDrag) {
this.enableTabRearrangeView(true);
}
browser.style.opacity = '.2';
event.dataTransfer.setData('text/plain', browser.closest('.browserSidebarContainer').id);
this._draggingTab = tab;
// Canvas setup for drag image
let scale = window.devicePixelRatio;
let canvas = this._dndCanvas;
if (!canvas) {
@@ -294,8 +347,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
// On Windows and Mac we can update the drag image during a drag
// using updateDragImage. On Linux, we can use a panel.
if (platform === 'win' || platform === 'macosx') {
captureListener = function () {
dt.updateDragImage(canvas, dragImageOffset, dragImageOffset);
captureListener = () => {
event.dataTransfer.updateDragImage(canvas, dragImageOffset, dragImageOffset);
};
} else {
// Create a panel to use it in setDragImage
@@ -326,7 +379,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
PageThumbs.captureToCanvas(browser, canvas).catch((e) => console.error(e));
dragImageOffset = dragImageOffset * scale;
}
dt.setDragImage(toDrag, dragImageOffset, dragImageOffset);
event.dataTransfer.setDragImage(toDrag, dragImageOffset, dragImageOffset);
return true;
};
@@ -364,10 +417,28 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
};
onBrowserDragEnd = (event) => {
this.dropZone.removeAttribute('enabled');
const draggingBrowser = this._draggingTab.linkedBrowser;
draggingBrowser.style.opacity = '.85';
this.dropZone?.removeAttribute('enabled');
// If we don't have drag state, just clean up what we can
if (!this._dragState) {
this._draggingTab = null;
return;
}
const { tab, browser, isSplitHeaderDrag } = this._dragState;
if (browser) {
browser.style.opacity = isSplitHeaderDrag ? '1' : '.85';
}
// Handle split view specific cleanup
if (isSplitHeaderDrag) {
this.disableTabRearrangeView(event);
}
// Clear state
this._draggingTab = null;
this._dragState = null;
};
_oppositeSide(side) {
@@ -476,10 +547,12 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
*/
resetTabState(tab, forUnsplit) {
tab.splitView = false;
tab.removeAttribute('split-view');
tab.linkedBrowser.zenModeActive = false;
const container = tab.linkedBrowser.closest('.browserSidebarContainer');
this._removeHeader(container);
this.resetContainerStyle(container);
container.removeEventListener('click', this.handleTabEvent);
container.removeEventListener('mousedown', this.handleTabEvent);
container.removeEventListener('mouseover', this.handleTabEvent);
if (!forUnsplit) {
tab.linkedBrowser.docShellIsActive = false;
@@ -631,7 +704,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
* @param {string} gridType - The type of grid layout.
*/
splitTabs(tabs, gridType) {
tabs = tabs.filter((tab) => !tab.hasAttribute('zen-empty-tab'));
if (tabs.length < 2) {
return;
}
@@ -667,6 +739,16 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
};
this._data.push(splitData);
window.gBrowser.selectedTab = tabs[0];
// Add tabs to the split view group
let splitGroup = this._getSplitViewGroup(tabs);
if (splitGroup) {
for (const tab of tabs) {
if (!tab.group || tab.group !== splitGroup) {
gBrowser.moveTabToGroup(tab, splitGroup);
}
}
}
this.activateSplitView(splitData);
}
@@ -770,11 +852,45 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
applyGridToTabs(tabs) {
tabs.forEach((tab, index) => {
tab.splitView = true;
const container = tab.linkedBrowser.closest('.browserSidebarContainer');
tab.setAttribute('split-view', 'true');
const container = tab.linkedBrowser.closest('.browserContainer');
if (!container.querySelector('.zen-view-splitter-header')) {
// insert a header into the container
const header = this._createHeader(container);
container.insertBefore(header, container.firstChild);
}
this.styleContainer(container);
});
}
/**
* Creates a header for the tab.
* @param container
* @returns {*|!Element|HTMLElement|HTMLUnknownElement|HTMLDirectoryElement|HTMLFontElement|HTMLFrameElement|HTMLFrameSetElement|HTMLPreElement|HTMLMarqueeElement|HTMLParamElement}
* @private
*/
_createHeader(container) {
const header = document.createElement('div');
header.classList.add('zen-view-splitter-header');
const dragHandle = document.createElement('div');
dragHandle.classList.add('zen-view-splitter-drag-handle');
header.appendChild(dragHandle);
const removeButton = document.createXULElement('toolbarbutton');
removeButton.classList.add('zen-tab-unsplit-button');
removeButton.addEventListener('click', () => {
this.removeTabFromSplit(container);
});
header.appendChild(removeButton);
return header;
}
_removeHeader(container) {
const header = container.querySelector('.zen-view-splitter-header');
if (header) {
header.remove();
}
}
/**
* Apply grid layout to tabBrowserPanel
*
@@ -883,7 +999,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
*/
styleContainer(container) {
container.setAttribute('zen-split-anim', 'true');
container.addEventListener('click', this.handleTabEvent);
container.addEventListener('mousedown', this.handleTabEvent);
container.addEventListener('mouseover', this.handleTabEvent);
}
@@ -896,7 +1012,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
if (this.rearrangeViewEnabled || (event.type === 'mouseover' && !this.canChangeTabOnHover)) {
return;
}
const container = event.currentTarget;
const container = event.currentTarget.closest('.browserSidebarContainer');
const tab = window.gBrowser.tabs.find((t) => t.linkedBrowser.closest('.browserSidebarContainer') === container);
if (tab) {
window.gBrowser.selectedTab = tab;
@@ -977,9 +1093,15 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
const browser = tab.linkedBrowser.closest('.browserSidebarContainer');
if (active) {
browser.setAttribute('zen-split', 'true');
browser.addEventListener('dragstart', this.onBrowserDragStart);
browser.addEventListener('dragend', this.onBrowserDragEnd);
} else {
browser.removeAttribute('zen-split');
browser.removeAttribute('style');
browser.removeEventListener('dragstart', this.onBrowserDragStart);
browser.removeEventListener('dragend', this.onBrowserDragEnd);
}
}
}
@@ -1136,6 +1258,134 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
: [gBrowser.selectedTab, tabs[nextTabIndex]];
this.splitTabs(selected_tabs, gridType);
}
/**
* @description removes the tab from the split
* @param container - The container element
*/
removeTabFromSplit = (container) => {
const browser = container.querySelector('browser');
if (browser) {
const tab = gBrowser.getTabForBrowser(browser);
if (tab) {
const groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
if (groupIndex >= 0) {
this.removeTabFromGroup(tab, groupIndex, true);
}
}
}
};
/**
* @description moves the tab to the split view if dragged on a browser
* @param event - The event
* @param draggedTab - The dragged tab
* @returns {boolean} true if the tab was moved to the split view
*/
moveTabToSplitView(event, draggedTab) {
const dropTarget = document.elementFromPoint(event.clientX, event.clientY);
const browser = dropTarget?.closest('browser');
if (!browser) {
return false;
}
const droppedOnTab = gBrowser.getTabForBrowser(browser);
if (droppedOnTab && droppedOnTab !== draggedTab) {
// Calculate which side of the target browser the drop occurred
const browserRect = browser.getBoundingClientRect();
const hoverSide = this.calculateHoverSide(event.clientX, event.clientY, browserRect);
if (droppedOnTab.splitView) {
// Add to existing split view
const groupIndex = this._data.findIndex((group) => group.tabs.includes(droppedOnTab));
const group = this._data[groupIndex];
if (!group.tabs.includes(draggedTab)) {
// First move the tab to the split view group
let splitGroup = droppedOnTab.group;
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
gBrowser.moveTabToGroup(draggedTab, splitGroup);
}
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
const parentNode = droppedOnSplitNode.parent;
// Then add the tab to the split view
group.tabs.push(draggedTab);
// If dropping on a side, create a new split in that direction
if (hoverSide !== 'center') {
const splitDirection = hoverSide === 'left' || hoverSide === 'right' ? 'row' : 'column';
if (parentNode.direction !== splitDirection) {
this.splitIntoNode(droppedOnSplitNode, new SplitLeafNode(draggedTab, 50), hoverSide, 0.5);
} else {
this.addTabToSplit(draggedTab, parentNode);
}
} else {
this.addTabToSplit(draggedTab, group.layoutTree);
}
this.activateSplitView(group, true);
}
} else {
// Create new split view with layout based on drop position
let gridType;
switch (hoverSide) {
case 'left':
case 'right':
gridType = 'vsep';
break;
case 'top':
case 'bottom':
gridType = 'hsep';
break;
default:
gridType = 'grid';
}
// Put tabs in correct order based on drop side
const tabs = ['left', 'top'].includes(hoverSide) ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab];
this.splitTabs(tabs, gridType);
}
}
return true;
}
/**
* Gets or creates a tab group for split view tabs
* @param {Array} tabs Initial tabs to add to the group if creating new
* @returns {TabGroup} The tab group for split view tabs
*/
_getSplitViewGroup(tabs) {
// if any of tabs is pinned, return null
if (tabs.some((tab) => tab.pinned)) {
return null;
}
// Try to find an existing split view group
let splitGroup = gBrowser.tabGroups.find(
(group) => group.getAttribute('split-view-group') && group.tabs.some((tab) => tabs.includes(tab) && tab.splitView)
);
if (splitGroup) {
return splitGroup;
}
// We can't create an empty group, so only create if we have tabs
if (tabs?.length) {
// Create a new group with the initial tabs
const group = gBrowser.addTabGroup(tabs, {
label: '',
showCreateUI: false,
});
group.setAttribute('split-view-group', true);
}
return null;
}
}
window.gZenViewSplitter = new ZenViewSplitter();

View File

@@ -2274,12 +2274,16 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
const containers = [essentialsContainer, ...pinnedContainers, ...normalContainers];
for (const container of containers) {
for (const tab of container.children) {
if (tab.tagName === 'tab' || tab.tagName == 'tab-group') {
if (tab.tagName === 'tab') {
tabs.push(tab);
const glance = tab.querySelector('.tabbrowser-tab[glance-id]');
if (glance) {
tabs.push(glance);
}
} else if (tab.tagName == 'tab-group') {
for (const groupTab of tab.tabs) {
tabs.push(groupTab);
}
}
}
}

View File

@@ -1,8 +1,13 @@
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..05dbb00375523819163bcd225074117ef02b8b37 100644
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..8b0781488e8f250666f1d542e3aa0cb34e018f24 100644
--- a/browser/components/sessionstore/TabState.sys.mjs
+++ b/browser/components/sessionstore/TabState.sys.mjs
@@ -84,6 +84,14 @@ class _TabState {
@@ -80,10 +80,18 @@ class _TabState {
tabData.muteReason = tab.muteReason;
}
- if (tab.group) {
+ if (tab.group && !tab.group.hasAttribute("split-view-group")) {
tabData.groupId = tab.group.id;
}

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c7547437c9f 100644
index 628aa6596627c85efe361fc1ece8fd58f7ee653e..490762fa04b9d37d8d7dee3e8dc22e220fe02955 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -412,11 +412,50 @@
@@ -194,7 +194,15 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Additionally send pinned tab events
if (pinned) {
this._notifyPinnedStatus(t);
@@ -3367,6 +3431,24 @@
@@ -2918,6 +2982,7 @@
id = `${Date.now()}-${Math.round(Math.random() * 100)}`;
}
let group = this._createTabGroup(id, color, false, label);
+ group.pinned = tabs.some(tab => tab.pinned);
this.tabContainer.insertBefore(
group,
insertBefore?.group ?? insertBefore
@@ -3367,6 +3432,24 @@
) {
tabWasReused = true;
tab = this.selectedTab;
@@ -219,7 +227,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (!tabData.pinned) {
this.unpinTab(tab);
} else {
@@ -3380,6 +3462,7 @@
@@ -3380,6 +3463,7 @@
restoreTabsLazily && !select && !tabData.pinned;
let url = "about:blank";
@@ -227,7 +235,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (tabData.entries?.length) {
let activeIndex = (tabData.index || tabData.entries.length) - 1;
// Ensure the index is in bounds.
@@ -3415,7 +3498,24 @@
@@ -3415,7 +3499,24 @@
skipLoad: true,
preferredRemoteType,
});
@@ -253,7 +261,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (select) {
tabToSelect = tab;
}
@@ -3428,8 +3528,8 @@
@@ -3428,8 +3529,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) {
@@ -264,7 +272,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
tab.toggleAttribute("pinned", true);
this.tabContainer._invalidateCachedTabs();
// Then ensure all the tab open/pinning information is sent.
@@ -3693,7 +3793,7 @@
@@ -3693,7 +3794,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.
@@ -273,7 +281,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (
!bulkOrderedOpen &&
((openerTab &&
@@ -3744,7 +3844,7 @@
@@ -3744,7 +3845,7 @@
}
/** @type {MozTabbrowserTab|undefined} */
@@ -282,7 +290,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
this.tabContainer._invalidateCachedTabs();
if (tabGroup) {
@@ -4059,6 +4159,9 @@
@@ -4059,6 +4160,9 @@
return;
}
@@ -292,7 +300,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
this.removeTabs(selectedTabs);
}
@@ -4391,6 +4494,7 @@
@@ -4391,6 +4495,7 @@
skipSessionStore,
} = {}
) {
@@ -300,7 +308,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (UserInteraction.running("browser.tabs.opening", window)) {
UserInteraction.finish("browser.tabs.opening", window);
}
@@ -4407,6 +4511,12 @@
@@ -4407,6 +4512,12 @@
TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
}
@@ -313,7 +321,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
@@ -4421,7 +4531,9 @@
@@ -4421,7 +4532,9 @@
// frame created for it (for example, by updating the visually selected
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
@@ -324,7 +332,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -4435,7 +4547,6 @@
@@ -4435,7 +4548,6 @@
TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
return;
}
@@ -332,7 +340,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
let lockTabSizing =
!this.tabContainer.verticalMode &&
!aTab.pinned &&
@@ -4574,14 +4685,14 @@
@@ -4574,14 +4686,14 @@
!!this.tabsInCollapsedTabGroups.length;
if (
aTab.visible &&
@@ -349,7 +357,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (closeWindow) {
// We've already called beforeunload on all the relevant tabs if we get here,
@@ -4605,6 +4716,7 @@
@@ -4605,6 +4717,7 @@
newTab = true;
}
@@ -357,7 +365,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
@@ -4645,9 +4757,7 @@
@@ -4645,9 +4758,7 @@
aTab._mouseleave();
if (newTab) {
@@ -368,7 +376,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
} else {
TabBarVisibility.update();
}
@@ -4776,6 +4886,8 @@
@@ -4776,6 +4887,8 @@
this.tabs[i]._tPos = i;
}
@@ -377,7 +385,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (!this._windowIsClosing) {
if (wasPinned) {
this.tabContainer._positionPinnedTabs();
@@ -4994,7 +5106,7 @@
@@ -4994,7 +5107,7 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -386,7 +394,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
}
// Try to find a remaining tab that comes after the given tab
@@ -5016,7 +5128,7 @@
@@ -5016,7 +5129,7 @@
}
if (tab) {
@@ -395,7 +403,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
}
// If no qualifying visible tab was found, see if there is a tab in
@@ -5434,10 +5546,10 @@
@@ -5434,10 +5547,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -408,7 +416,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
@@ -5675,7 +5787,7 @@
@@ -5675,7 +5788,7 @@
// Don't allow mixing pinned and unpinned tabs.
if (aTab.pinned) {
@@ -417,16 +425,27 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
} else {
aIndex = Math.max(aIndex, this.pinnedTabCount);
}
@@ -5684,7 +5796,7 @@
@@ -5684,8 +5797,8 @@
}
this._handleTabMove(aTab, () => {
- let neighbor = this.tabs[aIndex];
- if (forceStandaloneTab && neighbor.group) {
+ let neighbor = this.tabs.filter(tab => !tab.hasAttribute("zen-glance-tab"))[aIndex];
if (forceStandaloneTab && neighbor.group) {
+ if (forceStandaloneTab && neighbor.group || (neighbor.group && neighbor.group.hasAttribute("split-view-group"))) {
neighbor = neighbor.group;
}
@@ -5721,6 +5833,8 @@
if (neighbor && aIndex >= aTab._tPos) {
@@ -5697,7 +5810,7 @@
}
moveTabToGroup(aTab, aGroup) {
- if (aTab.pinned) {
+ if (aTab.pinned != !!aGroup.pinned && !aTab.hasAttribute("zen-essential")) {
return;
}
if (aTab.group && aTab.group.id === aGroup.id) {
@@ -5721,6 +5834,8 @@
moveActionCallback();
@@ -435,7 +454,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
@@ -5771,7 +5885,7 @@
@@ -5771,7 +5886,7 @@
createLazyBrowser,
};
@@ -444,7 +463,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
params.pinned = true;
}
@@ -7415,6 +7529,7 @@
@@ -7415,6 +7530,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -452,7 +471,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -8381,7 +8496,7 @@ var TabContextMenu = {
@@ -8381,7 +8497,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !multiselectionContext;
@@ -461,7 +480,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Move Tab items
let contextMoveTabOptions = document.getElementById(
"context_moveTabOptions"
@@ -8414,7 +8529,7 @@ var TabContextMenu = {
@@ -8414,7 +8530,7 @@ var TabContextMenu = {
let contextMoveTabToStart = document.getElementById("context_moveToStart");
let isFirstTab =
tabsToMove[0] == visibleTabs[0] ||
@@ -470,7 +489,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
contextMoveTabToStart.disabled = isFirstTab && allSelectedTabsAdjacent;
document.getElementById("context_openTabInWindow").disabled =
@@ -8647,6 +8762,7 @@ var TabContextMenu = {
@@ -8647,6 +8763,7 @@ var TabContextMenu = {
if (this.contextTab.multiselected) {
gBrowser.removeMultiSelectedTabs();
} else {

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990d029cd67 100644
index fa96568d366fd3608f9bd583fa793150bd815c8b..76b22ad907fe8b858300ec104af2b9b7b1d29a0e 100644
--- a/browser/components/tabbrowser/content/tabs.js
+++ b/browser/components/tabbrowser/content/tabs.js
@@ -94,7 +94,7 @@
@@ -91,7 +91,18 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
@@ -1285,7 +1297,8 @@
@@ -1282,10 +1294,19 @@
this._finishMoveTogetherSelectedTabs(draggedTab);
this._finishAnimateTabMove();
+ if (event.ctrlKey && !dt.mozUserCancelled && dt.dropEffect == "none" && !this._isCustomizing) {
+ const moved = gZenViewSplitter.moveTabToSplitView(event, draggedTab);
+ if (moved) {
+ delete draggedTab._dragData;
+ return;
+ }
+ }
+
if (
dt.mozUserCancelled ||
dt.dropEffect != "none" ||
@@ -101,7 +112,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
) {
delete draggedTab._dragData;
return;
@@ -1517,7 +1530,7 @@
@@ -1517,7 +1538,7 @@
}
get newTabButton() {
@@ -110,7 +121,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
get verticalMode() {
@@ -1537,7 +1550,7 @@
@@ -1537,7 +1558,7 @@
if (this.#allTabs) {
return this.#allTabs;
}
@@ -119,7 +130,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
// remove arrowScrollbox periphery element
children.pop();
@@ -1551,14 +1564,29 @@
@@ -1551,14 +1572,29 @@
}
this.#allTabs = [
@@ -151,7 +162,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
return children.filter(node => node.tagName == "tab-group");
}
@@ -1579,7 +1607,7 @@
@@ -1579,7 +1615,7 @@
*/
get visibleTabs() {
if (!this.#visibleTabs) {
@@ -160,7 +171,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
return this.#visibleTabs;
}
@@ -1613,10 +1641,8 @@
@@ -1613,10 +1649,8 @@
return this.#focusableItems;
}
@@ -173,7 +184,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let focusableItems = [];
for (let child of children) {
@@ -1632,6 +1658,7 @@
@@ -1632,6 +1666,7 @@
}
this.#focusableItems = [
@@ -181,7 +192,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
...verticalPinnedTabsContainer.children,
...focusableItems,
];
@@ -1642,6 +1669,7 @@
@@ -1642,6 +1677,7 @@
_invalidateCachedTabs() {
this.#allTabs = null;
this._invalidateCachedVisibleTabs();
@@ -189,7 +200,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
_invalidateCachedVisibleTabs() {
@@ -1656,8 +1684,8 @@
@@ -1656,8 +1692,8 @@
#isContainerVerticalPinnedExpanded(tab) {
return (
this.verticalMode &&
@@ -200,7 +211,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
);
}
@@ -1672,7 +1700,7 @@
@@ -1672,7 +1708,7 @@
if (node == null) {
// We have a container for non-tab elements at the end of the scrollbox.
@@ -209,7 +220,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
node.before(tab);
@@ -1772,7 +1800,7 @@
@@ -1772,7 +1808,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.
@@ -218,7 +229,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
const newTab2 = this.newTabButton;
const newTabVertical = document.getElementById(
"vertical-tabs-newtab-button"
@@ -1855,7 +1883,7 @@
@@ -1855,7 +1891,7 @@
let rect = ele => {
return window.windowUtils.getBoundsWithoutFlushing(ele);
};
@@ -227,7 +238,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (tab && rect(tab).width <= this._tabClipWidth) {
this.setAttribute("closebuttons", "activetab");
} else {
@@ -1867,10 +1895,12 @@
@@ -1867,10 +1903,12 @@
_handleTabSelect(aInstant) {
let selectedTab = this.selectedItem;
@@ -240,7 +251,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
selectedTab._notselectedsinceload = false;
}
@@ -1882,7 +1912,7 @@
@@ -1882,7 +1920,7 @@
return;
}
@@ -249,7 +260,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (!tabs.length) {
return;
}
@@ -1918,7 +1948,7 @@
@@ -1918,7 +1956,7 @@
if (isEndTab && !this._hasTabTempMaxWidth) {
return;
}
@@ -258,7 +269,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
// Force tabs to stay the same width, unless we're closing the last tab,
// which case we need to let them expand just enough so that the overall
// tabbar width is the same.
@@ -1933,7 +1963,7 @@
@@ -1933,7 +1971,7 @@
let tabsToReset = [];
for (let i = numPinned; i < tabs.length; i++) {
let tab = tabs[i];
@@ -267,7 +278,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (!isEndTab) {
// keep tabs the same width
tab.style.transition = "none";
@@ -1999,16 +2029,15 @@
@@ -1999,16 +2037,15 @@
// Move pinned tabs to another container when the tabstrip is toggled to vertical
// and when session restore code calls _positionPinnedTabs; update styling whenever
// the number of pinned tabs changes.
@@ -290,7 +301,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
}
@@ -2016,9 +2045,7 @@
@@ -2016,9 +2053,7 @@
}
_resetVerticalPinnedTabs() {
@@ -301,7 +312,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (!verticalTabsContainer.children.length) {
return;
@@ -2031,8 +2058,8 @@
@@ -2031,8 +2066,8 @@
}
_positionPinnedTabs() {
@@ -312,7 +323,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let absPositionHorizontalTabs =
this.overflowing && tabs.length > numPinned && numPinned > 0;
@@ -2042,6 +2069,7 @@
@@ -2042,6 +2077,7 @@
if (this.verticalMode) {
this._updateVerticalPinnedTabs();
} else if (absPositionHorizontalTabs) {
@@ -320,7 +331,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let layoutData = this._pinnedTabsLayoutCache;
let uiDensity = document.documentElement.getAttribute("uidensity");
if (!layoutData || layoutData.uiDensity != uiDensity) {
@@ -2113,7 +2141,7 @@
@@ -2113,7 +2149,7 @@
return;
}
@@ -329,7 +340,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let directionX = screenX > dragData.animLastScreenX;
let directionY = screenY > dragData.animLastScreenY;
@@ -2296,10 +2324,11 @@
@@ -2296,10 +2332,11 @@
}
let pinned = draggedTab.pinned;
@@ -345,7 +356,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
);
if (this.#rtlMode) {
@@ -2541,8 +2570,9 @@
@@ -2541,8 +2578,9 @@
);
}
@@ -357,7 +368,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
return;
}
@@ -2604,7 +2634,7 @@
@@ -2604,7 +2642,7 @@
movingTab._moveTogetherSelectedTabsData.newIndex = movingTabNewIndex;
movingTab._moveTogetherSelectedTabsData.animate = false;
};
@@ -366,7 +377,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
@@ -2707,9 +2737,9 @@
@@ -2707,9 +2745,9 @@
function newIndex(aTab, index) {
// Don't allow mixing pinned and unpinned tabs.
if (aTab.pinned) {
@@ -378,7 +389,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
}
@@ -2793,7 +2823,7 @@
@@ -2793,7 +2831,7 @@
}
_notifyBackgroundTab(aTab) {