chore: Finished basic support for split views, b=no-bug, c=folders, split-view

This commit is contained in:
mr. m
2025-12-15 00:54:21 +01:00
parent fa1693d16b
commit 8b33fe91eb
4 changed files with 37 additions and 20 deletions

View File

@@ -1131,7 +1131,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const dropElementGroup = dropElement?.isZenFolder ? dropElement : dropElement?.group;
const isSplitGroup = dropElement?.group?.hasAttribute('split-view-group');
let firstGroupElem = dropElementGroup.querySelector('.zen-tab-group-start').nextElementSibling;
let firstGroupElem =
dropElementGroup?.querySelector('.zen-tab-group-start')?.nextElementSibling;
if (gBrowser.isTabGroup(firstGroupElem)) firstGroupElem = firstGroupElem.labelElement;
const isInMiddleZone =

View File

@@ -247,7 +247,7 @@ export class nsZenSessionManager {
*/
restoreNewWindow(aWindow, SessionStoreInternal, resolvePromise) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled) {
return;
return resolvePromise();
}
this.log('Restoring new window with Zen session data');
const state = lazy.SessionStore.getCurrentState(true);

View File

@@ -36,8 +36,8 @@ const EVENTS = [
'TabGroupRemoved',
'TabGroupMoved',
//'ZenTabRemovedFromSplit',
//'ZenSplitViewTabsSplit',
'ZenTabRemovedFromSplit',
'ZenSplitViewTabsSplit',
'TabSelect',
@@ -242,10 +242,14 @@ class nsZenWindowSync {
*/
#handleNextEvent(aEvent) {
const handler = `on_${aEvent.type}`;
if (typeof this[handler] === 'function') {
return this[handler](aEvent) || Promise.resolve();
} else {
console.warn(`ZenWindowSync: No handler for event type: ${aEvent.type}`);
try {
if (typeof this[handler] === 'function') {
return this[handler](aEvent) || Promise.resolve();
} else {
throw new Error(`No handler for event type: ${aEvent.type}`);
}
} catch (e) {
return Promise.reject(e);
}
}
@@ -483,7 +487,7 @@ class nsZenWindowSync {
// Restore the listeners for the swapped in tab.
if (!onClose) {
tabListener = new otherTabBrowser.zenTabProgressListener(aTab, otherBrowser, false, false);
tabListener = new otherTabBrowser.zenTabProgressListener(aTab, otherBrowser, true, false);
otherTabBrowser._tabListeners.set(aTab, tabListener);
const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
@@ -527,10 +531,13 @@ class nsZenWindowSync {
this.log(`Swapping docshells between windows for tab ${aOurTab.id}`);
aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(aOurTab, aOtherTab, false);
this.#makeSureTabSyncsPermanentKey(aOurTab);
if (!aOtherTab.hasAttribute('busy')) {
aOurTab.removeAttribute('busy');
}
},
onClose
);
const kAttributesToRemove = ['muted', 'soundplaying', 'sharing', 'pictureinpicture'];
const kAttributesToRemove = ['muted', 'soundplaying', 'sharing', 'pictureinpicture', 'busy'];
// swapBrowsersAndCloseOther already takes care of transferring attributes like 'muted',
// but we need to manually remove some attributes from the other tab.
for (let attr of kAttributesToRemove) {
@@ -676,11 +683,12 @@ class nsZenWindowSync {
*
* @param {Window} aWindow - The window that triggered the event.
* @param {Object} aPreviousTab - The previously selected tab.
* @param {boolean} ignoreSameTab - Indicates if the same tab should be ignored.
*/
async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) {
async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null, ignoreSameTab = false) {
// On some occasions, such as when closing a window, this
// function might be called multiple times for the same tab.
if (aWindow.gBrowser.selectedTab === this.#lastSelectedTab) {
if (aWindow.gBrowser.selectedTab === this.#lastSelectedTab && !ignoreSameTab) {
return;
}
if (aPreviousTab?._zenContentsVisible) {
@@ -786,11 +794,13 @@ class nsZenWindowSync {
continue;
}
newTab._zenContentsVisible = true;
gBrowser.setTabTitle(newTab);
gZenWorkspaces.moveTabToWorkspace(newTab, aWorkspaceId);
}
}
if (success) {
aWindow.close();
win.gBrowser.selectedTab.linkedBrowser.focus();
}
};
if (!win) {
@@ -809,13 +819,11 @@ class nsZenWindowSync {
on_TabOpen(aEvent) {
const tab = aEvent.target;
const window = tab.ownerGlobal;
if (tab.selected) {
tab._zenContentsVisible = true;
}
if (tab.id) {
// This tab was opened as part of a sync operation.
return;
}
tab._zenContentsVisible = true;
tab.id = this.#newTabSyncId;
this.#runOnAllWindows(window, (win) => {
const newTab = win.gBrowser.addTrustedTab('about:blank', {
@@ -1002,12 +1010,16 @@ class nsZenWindowSync {
.map((tab) => this.#getItemFromWindow(win, tab.id))
.filter(Boolean);
if (otherWindowTabs.length > 0 && win.gZenViewSplitter) {
const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, 'grid');
const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, 'grid', -1);
if (group) {
group.id = tabGroup.id;
let otherTabGroup = group.tabs[0].group;
otherTabGroup.id = tabGroup.id;
this.#syncItemWithOriginal(aEvent.target, otherTabGroup, win, SYNC_FLAG_MOVE);
}
}
});
return this.#onTabSwitchOrWindowFocus(window, null, /* ignoreSameTab = */ true);
}
}

View File

@@ -1084,8 +1084,12 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
*
* @param {Tab[]} tabs - The tabs to split.
* @param {string|undefined} gridType - The type of grid layout.
* @param {number} initialIndex - The index of the initially active tab.
* use -1 to avoid selecting any tab.
* @return {object|undefined} The split view data or undefined if the split was not performed.
*/
splitTabs(tabs, gridType, initialIndex = 0) {
const tabIndexToUse = Math.max(0, initialIndex);
return this.#withoutSplitViewTransition(() => {
// TODO: Add support for splitting essential tabs
tabs = tabs.filter((t) => !t.hidden && !t.hasAttribute('zen-empty-tab'));
@@ -1095,7 +1099,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const existingSplitTab = tabs.find((tab) => tab.splitView);
if (existingSplitTab) {
this._moveTabsToContainer(tabs, tabs[initialIndex]);
this._moveTabsToContainer(tabs, tabs[tabIndexToUse]);
const groupIndex = this._data.findIndex((group) => group.tabs.includes(existingSplitTab));
const group = this._data[groupIndex];
const gridTypeChange = gridType && group.gridType !== gridType;
@@ -1150,8 +1154,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
layoutTree: this.calculateLayoutTree(tabs, gridType),
};
this._data.push(splitData);
if (!this._sessionRestoring) {
window.gBrowser.selectedTab = tabs[initialIndex] ?? tabs[0];
if (!this._sessionRestoring && initialIndex >= 0) {
window.gBrowser.selectedTab = tabs[tabIndexToUse] ?? tabs[0];
}
// Add tabs to the split view group