mirror of
https://github.com/zen-browser/desktop.git
synced 2026-04-20 22:35:39 +00:00
feat: Store and restore split view layout tree, p=#11862
* feat: Store and restore split view layout tree * fix: add backwards compatibility * fix: Formatting * fix: Don't activate split view when restoring session * feat: Add id to unsynced tabs * fix: Formatting * refactor: Streamline unsynced window event handling * feat: Make sure to duplicate pinend tabs when splitting, b=no-bug, c=split-view --------- Co-authored-by: mr. m <mr.m@tuta.com>
This commit is contained in:
@@ -22,8 +22,8 @@ XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.window-sync.log",
|
||||
|
||||
const OBSERVING = ["browser-window-before-show"];
|
||||
const INSTANT_EVENTS = ["SSWindowClosing"];
|
||||
const UNSYNCED_WINDOW_EVENTS = ["TabOpen"];
|
||||
const EVENTS = [
|
||||
"TabOpen",
|
||||
"TabClose",
|
||||
|
||||
"ZenTabIconChanged",
|
||||
@@ -47,6 +47,7 @@ const EVENTS = [
|
||||
|
||||
"focus",
|
||||
...INSTANT_EVENTS,
|
||||
...UNSYNCED_WINDOW_EVENTS,
|
||||
];
|
||||
|
||||
// Flags acting as an enum for sync types.
|
||||
@@ -163,6 +164,9 @@ class nsZenWindowSync {
|
||||
) {
|
||||
this.log("Not syncing new window due to unsynced argument or existing synced windows");
|
||||
aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true");
|
||||
for (let eventName of UNSYNCED_WINDOW_EVENTS) {
|
||||
aWindow.addEventListener(eventName, this, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
aWindow.gZenWindowSync = this;
|
||||
@@ -254,7 +258,7 @@ class nsZenWindowSync {
|
||||
const window = aEvent.currentTarget.ownerGlobal;
|
||||
if (
|
||||
!window.gZenStartup.isReady ||
|
||||
window.gZenWorkspaces?.privateWindowOrDisabled ||
|
||||
!window.gZenWorkspaces?.shouldHaveWorkspaces ||
|
||||
window._zenClosingWindow
|
||||
) {
|
||||
return;
|
||||
@@ -944,12 +948,17 @@ class nsZenWindowSync {
|
||||
on_TabOpen(aEvent) {
|
||||
const tab = aEvent.target;
|
||||
const window = tab.ownerGlobal;
|
||||
const isUnsyncedWindow = window.document.documentElement.hasAttribute("zen-unsynced-window");
|
||||
|
||||
if (tab.id) {
|
||||
// This tab was opened as part of a sync operation.
|
||||
return;
|
||||
}
|
||||
tab._zenContentsVisible = true;
|
||||
tab.id = this.#newTabSyncId;
|
||||
if (isUnsyncedWindow) {
|
||||
return;
|
||||
}
|
||||
this.#runOnAllWindows(window, (win) => {
|
||||
const newTab = win.gBrowser.addTrustedTab("about:blank", {
|
||||
animate: true,
|
||||
|
||||
@@ -250,6 +250,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
return element;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
onBrowserDragOverToSplit(event) {
|
||||
if (this.fakeBrowser) {
|
||||
return;
|
||||
@@ -275,7 +276,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
!this._lastOpenedTab ||
|
||||
(this._lastOpenedTab.getAttribute("zen-workspace-id") !==
|
||||
draggedTab.getAttribute("zen-workspace-id") &&
|
||||
!this._lastOpenedTab.hasAttribute("zen-essential"))
|
||||
!this._lastOpenedTab.hasAttribute("zen-essential") &&
|
||||
!draggedTab.hasAttribute("zen-essential"))
|
||||
) {
|
||||
this._lastOpenedTab = gBrowser.selectedTab;
|
||||
}
|
||||
@@ -1229,7 +1231,19 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
|
||||
gridType ??= "grid";
|
||||
|
||||
// Add tabs to the split view group
|
||||
let splitGroup = this._getSplitViewGroup(tabs);
|
||||
const groupId = splitGroup?.id;
|
||||
if (splitGroup) {
|
||||
for (const tab of tabs) {
|
||||
if (!tab.group || tab.group !== splitGroup) {
|
||||
gBrowser.moveTabToExistingGroup(tab, splitGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const splitData = {
|
||||
groupId,
|
||||
tabs,
|
||||
gridType,
|
||||
layoutTree: this.calculateLayoutTree(tabs, gridType),
|
||||
@@ -1239,20 +1253,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
window.gBrowser.selectedTab = tabs[tabIndexToUse] ?? 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.moveTabToExistingGroup(tab, splitGroup);
|
||||
}
|
||||
}
|
||||
if (!this._sessionRestoring) {
|
||||
this.activateSplitView(splitData);
|
||||
}
|
||||
|
||||
if (this._sessionRestoring) {
|
||||
return;
|
||||
}
|
||||
this.activateSplitView(splitData);
|
||||
this.#dispatchItemEvent("ZenSplitViewTabsSplit", splitGroup);
|
||||
// eslint-disable-next-line consistent-return
|
||||
return splitData;
|
||||
@@ -1827,7 +1831,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
return false;
|
||||
}
|
||||
|
||||
const droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(gBrowser.getTabForBrowser(browser));
|
||||
let droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(gBrowser.getTabForBrowser(browser));
|
||||
if (droppedOnTab === this._draggingTab) {
|
||||
this.createEmptySplit(dropSide == "right");
|
||||
return true;
|
||||
@@ -1846,6 +1850,25 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
// const hoverSide = this.calculateHoverSide(event.clientX, event.clientY, browserRect);
|
||||
const hoverSide = dropSide;
|
||||
|
||||
// We are here if none of the tabs have been previously split
|
||||
// If there's ANY pinned tab on the list, we clone the pinned tab
|
||||
// state to all the tabs
|
||||
let tempTabs = [draggedTab, droppedOnTab];
|
||||
const allArePinned = tempTabs.every((tab) => tab.pinned);
|
||||
const thereIsOnePinned = tempTabs.some((tab) => tab.pinned);
|
||||
const thereIsOneEssential = tempTabs.some((tab) => tab.hasAttribute("zen-essential"));
|
||||
|
||||
if (thereIsOneEssential || (thereIsOnePinned && !allArePinned)) {
|
||||
for (let i = 0; i < tempTabs.length; i++) {
|
||||
const tab = tempTabs[i];
|
||||
if (tab.pinned) {
|
||||
tempTabs[i] = gBrowser.duplicateTab(tab, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[draggedTab, droppedOnTab] = tempTabs;
|
||||
|
||||
if (droppedOnTab.splitView) {
|
||||
// Add to existing split view
|
||||
const groupIndex = this._data.findIndex((group) => group.tabs.includes(droppedOnTab));
|
||||
@@ -1908,6 +1931,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
dropSide == "left" ? 0 : 1
|
||||
);
|
||||
}
|
||||
|
||||
gBrowser.selectedTab = draggedTab;
|
||||
}
|
||||
if (this._finishAllAnimatingPromise) {
|
||||
this._finishAllAnimatingPromise.then(() => {
|
||||
@@ -2007,31 +2032,79 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
|
||||
storeDataForSessionStore() {
|
||||
// We cant store any tab or browser elements in the session store
|
||||
// so we need to store the tab indexes and group indexes
|
||||
const data = this._data.map((group) => {
|
||||
const serializeNode = (node) => {
|
||||
if (node.tab) {
|
||||
return {
|
||||
type: "leaf",
|
||||
tabId: node.tab.id,
|
||||
sizeInParent: node.sizeInParent,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
groupId: group.tabs[0].group?.id,
|
||||
type: "splitter",
|
||||
direction: node.direction,
|
||||
sizeInParent: node.sizeInParent,
|
||||
children: node._children.map((child) => serializeNode(child)),
|
||||
};
|
||||
};
|
||||
|
||||
return this._data.map((group) => {
|
||||
const serializedTree = serializeNode(group.layoutTree);
|
||||
return {
|
||||
groupId: group.groupId,
|
||||
gridType: group.gridType,
|
||||
layoutTree: serializedTree,
|
||||
tabs: group.tabs.map((tab) => tab.id),
|
||||
};
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
restoreDataFromSessionStore(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sessionRestoring = true;
|
||||
// We can just get the tab group with document.getElementById(group.groupId)
|
||||
// and add the tabs to it
|
||||
for (const group of data) {
|
||||
const groupElement = document.getElementById(group.groupId);
|
||||
if (groupElement) {
|
||||
const tabs = groupElement.tabs;
|
||||
this.splitTabs(tabs, group.gridType);
|
||||
|
||||
for (const groupData of data) {
|
||||
const group = document.getElementById(groupData.groupId);
|
||||
|
||||
// Backwards compatibility
|
||||
if (!groupData?.layoutTree) {
|
||||
this.splitTabs(group.tabs, group.gridType);
|
||||
delete this._sessionRestoring;
|
||||
return;
|
||||
}
|
||||
|
||||
const deserializeNode = (nodeData) => {
|
||||
if (nodeData.type === "leaf") {
|
||||
const tab = document.getElementById(nodeData.tabId);
|
||||
if (!tab) {
|
||||
return null;
|
||||
}
|
||||
return new nsSplitLeafNode(tab, nodeData.sizeInParent);
|
||||
}
|
||||
|
||||
const splitter = new nsSplitNode(nodeData.direction, nodeData.sizeInParent);
|
||||
splitter._children = [];
|
||||
|
||||
for (const childData of nodeData.children) {
|
||||
const childNode = deserializeNode(childData);
|
||||
if (childNode) {
|
||||
childNode.parent = splitter;
|
||||
splitter._children.push(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
return splitter;
|
||||
};
|
||||
|
||||
const layout = deserializeNode(groupData.layoutTree);
|
||||
const splitData = this.splitTabs(group.tabs, groupData.gridType, -1);
|
||||
splitData.layoutTree = layout;
|
||||
}
|
||||
|
||||
delete this._sessionRestoring;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user