mirror of
https://github.com/zen-browser/desktop.git
synced 2026-08-28 09:21:32 +00:00
Refactor ZenViewSplitter to improve drag-and-drop functionality and enhance split view behavior
This commit is contained in:
@@ -62,6 +62,7 @@ var gZenUIManager = {
|
|||||||
}
|
}
|
||||||
tabs.style.removeProperty('flex');
|
tabs.style.removeProperty('flex');
|
||||||
tabs.style.maxHeight = height + 'px';
|
tabs.style.maxHeight = height + 'px';
|
||||||
|
gZenVerticalTabsManager.actualWindowButtons.removeAttribute('zen-has-hover');
|
||||||
},
|
},
|
||||||
|
|
||||||
get tabsWrapper() {
|
get tabsWrapper() {
|
||||||
|
|||||||
@@ -312,9 +312,6 @@ var gZenCompactModeManager = {
|
|||||||
{
|
{
|
||||||
element: gZenVerticalTabsManager.actualWindowButtons,
|
element: gZenVerticalTabsManager.actualWindowButtons,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
element: gZenVerticalTabsManager.actualWindowButtons,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
|
|
||||||
// Add drag over listener to the browser view
|
// Add drag over listener to the browser view
|
||||||
const tabBox = document.getElementById('tabbrowser-tabbox');
|
const tabBox = document.getElementById('tabbrowser-tabbox');
|
||||||
tabBox.addEventListener('dragenter', this.onBrowserDragOverToSplit.bind(this));
|
tabBox.addEventListener('dragover', this.onBrowserDragOverToSplit.bind(this));
|
||||||
tabBox.addEventListener('dragleave', this.onBrowserDragEndToSplit.bind(this));
|
this.onBrowserDragEndToSplit = this.onBrowserDragEndToSplit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
insertIntoContextMenu() {
|
insertIntoContextMenu() {
|
||||||
@@ -198,7 +198,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
// only show if we are 1/4 of the way to the edge
|
// only show if we are 1/4 of the way to the edge
|
||||||
const panelsRect = gBrowser.tabbox.getBoundingClientRect();
|
const panelsRect = gBrowser.tabbox.getBoundingClientRect();
|
||||||
const panelsWidth = panelsRect.width;
|
const panelsWidth = panelsRect.width;
|
||||||
if (event.clientX > (panelsWidth / 4) * 3) {
|
if (event.clientX > panelsWidth / 4 || event.clientX < panelsRect.left + 20) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const oldTab = this._lastOpenedTab;
|
const oldTab = this._lastOpenedTab;
|
||||||
@@ -211,12 +211,12 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
const panelsWidth = gBrowser.tabbox.getBoundingClientRect().width;
|
const panelsWidth = gBrowser.tabbox.getBoundingClientRect().width;
|
||||||
const halfWidth = panelsWidth / 2;
|
const halfWidth = panelsWidth / 2;
|
||||||
this.fakeBrowser = document.createXULElement('vbox');
|
this.fakeBrowser = document.createXULElement('vbox');
|
||||||
|
this.fakeBrowser.addEventListener('dragleave', this.onBrowserDragEndToSplit);
|
||||||
const padding = Services.prefs.getIntPref('zen.theme.content-element-separation', 0);
|
const padding = Services.prefs.getIntPref('zen.theme.content-element-separation', 0);
|
||||||
this.fakeBrowser.setAttribute('flex', '1');
|
this.fakeBrowser.setAttribute('flex', '1');
|
||||||
this.fakeBrowser.id = 'zen-split-view-fake-browser';
|
this.fakeBrowser.id = 'zen-split-view-fake-browser';
|
||||||
gBrowser.tabbox.appendChild(this.fakeBrowser);
|
gBrowser.tabbox.appendChild(this.fakeBrowser);
|
||||||
this.fakeBrowser.style.setProperty('--zen-split-view-fake-icon', `url(${draggedTab.getAttribute('image')})`);
|
this.fakeBrowser.style.setProperty('--zen-split-view-fake-icon', `url(${draggedTab.getAttribute('image')})`);
|
||||||
draggedTab._visuallySelected = true;
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
gZenUIManager.motion.animate(
|
gZenUIManager.motion.animate(
|
||||||
gBrowser.tabbox,
|
gBrowser.tabbox,
|
||||||
@@ -239,7 +239,9 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
easing: 'ease-out',
|
easing: 'ease-out',
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
]).then(() => {});
|
]).then(() => {
|
||||||
|
draggedTab._visuallySelected = true;
|
||||||
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,10 +265,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
if (!this.fakeBrowser) {
|
if (!this.fakeBrowser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if we are still in that 1/4 of the way to the edge then we should not hide the fake browser
|
|
||||||
if (event.clientX < (panelsWidth / 4) * 3 && event.clientX > panelsRect.left) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.fakeBrowser.classList.add('fade-out');
|
this.fakeBrowser.classList.add('fade-out');
|
||||||
gBrowser.selectedTab = this._draggingTab;
|
gBrowser.selectedTab = this._draggingTab;
|
||||||
this._draggingTab = null;
|
this._draggingTab = null;
|
||||||
@@ -365,14 +363,13 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
|
|
||||||
afterRearangeAction() {
|
afterRearangeAction() {
|
||||||
document.getElementById('zenSplitViewModifier').hidePopup();
|
document.getElementById('zenSplitViewModifier').hidePopup();
|
||||||
ConfirmationHint.show(document.getElementById('zen-split-views-box'), 'zen-split-view-modifier-enabled-toast', {
|
gZenUIManager.showToast('zen-split-view-modifier-enabled-toast', {
|
||||||
descriptionId: 'zen-split-view-modifier-enabled-toast-description',
|
descriptionId: 'zen-split-view-modifier-enabled-toast-description',
|
||||||
showDescription: true,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
afterRearangeRemove() {
|
afterRearangeRemove() {
|
||||||
ConfirmationHint.show(document.getElementById('zen-split-views-box'), 'zen-split-view-modifier-disabled-toast');
|
gZenUIManager.showToast('zen-split-view-modifier-disabled-toast');
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleWrapperDisplay(value) {
|
toggleWrapperDisplay(value) {
|
||||||
@@ -1434,7 +1431,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tabs = gBrowser.visibleTabs;
|
const tabs = gBrowser.visibleTabs;
|
||||||
if (tabs.length < 2) {
|
if (tabs.length < 2 || this.currentView >= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let nextTabIndex = tabs.indexOf(gBrowser.selectedTab) + 1;
|
let nextTabIndex = tabs.indexOf(gBrowser.selectedTab) + 1;
|
||||||
@@ -1464,6 +1461,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
const tab = gBrowser.getTabForBrowser(browser);
|
const tab = gBrowser.getTabForBrowser(browser);
|
||||||
if (tab) {
|
if (tab) {
|
||||||
const groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
|
const groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
|
||||||
|
this.deactivateCurrentSplitView();
|
||||||
if (groupIndex >= 0) {
|
if (groupIndex >= 0) {
|
||||||
this.removeTabFromGroup(tab, groupIndex, true);
|
this.removeTabFromGroup(tab, groupIndex, true);
|
||||||
}
|
}
|
||||||
@@ -1477,6 +1475,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
delete this._hasAnimated;
|
delete this._hasAnimated;
|
||||||
this.fakeBrowser.remove();
|
this.fakeBrowser.remove();
|
||||||
this.fakeBrowser = null;
|
this.fakeBrowser = null;
|
||||||
|
this._draggingTab._visuallySelected = false;
|
||||||
if (select) {
|
if (select) {
|
||||||
gBrowser.selectedTab = this._draggingTab;
|
gBrowser.selectedTab = this._draggingTab;
|
||||||
this._draggingTab = null;
|
this._draggingTab = null;
|
||||||
@@ -1492,99 +1491,106 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
|||||||
*/
|
*/
|
||||||
moveTabToSplitView(event, draggedTab) {
|
moveTabToSplitView(event, draggedTab) {
|
||||||
const canDrop = this._canDrop;
|
const canDrop = this._canDrop;
|
||||||
this._maybeRemoveFakeBrowser(false);
|
|
||||||
this._canDrop = false;
|
this._canDrop = false;
|
||||||
|
|
||||||
if (!canDrop) {
|
if (!canDrop || !this.fakeBrowser) {
|
||||||
|
this._maybeRemoveFakeBrowser(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.requestAnimationFrame(() => {
|
const containerRect = this.fakeBrowser.getBoundingClientRect();
|
||||||
const dropTarget = document.elementFromPoint(event.clientX, event.clientY);
|
const dropTarget = document.elementFromPoint((containerRect.left + containerRect.width) * 1.5, event.clientY);
|
||||||
const browser = dropTarget?.closest('browser');
|
const browser = dropTarget?.closest('browser');
|
||||||
|
|
||||||
if (!browser) {
|
if (!browser) {
|
||||||
return false;
|
this._maybeRemoveFakeBrowser(false);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
gBrowser.selectedTab = this._draggingTab;
|
gBrowser.selectedTab = this._draggingTab;
|
||||||
this._draggingTab = null;
|
this._draggingTab = null;
|
||||||
|
const browserContainer = draggedTab.linkedBrowser?.closest('.browserSidebarContainer');
|
||||||
|
if (browserContainer) {
|
||||||
|
browserContainer.style.opacity = '0';
|
||||||
|
}
|
||||||
|
|
||||||
const droppedOnTab = gBrowser.getTabForBrowser(browser);
|
const droppedOnTab = gBrowser.getTabForBrowser(browser);
|
||||||
if (droppedOnTab && droppedOnTab !== draggedTab) {
|
if (droppedOnTab && droppedOnTab !== draggedTab) {
|
||||||
// Calculate which side of the target browser the drop occurred
|
// Calculate which side of the target browser the drop occurred
|
||||||
// const browserRect = browser.getBoundingClientRect();
|
// const browserRect = browser.getBoundingClientRect();
|
||||||
// const hoverSide = this.calculateHoverSide(event.clientX, event.clientY, browserRect);
|
// const hoverSide = this.calculateHoverSide(event.clientX, event.clientY, browserRect);
|
||||||
const hoverSide = 'right';
|
const hoverSide = 'right';
|
||||||
|
|
||||||
if (droppedOnTab.splitView) {
|
if (droppedOnTab.splitView) {
|
||||||
// Add to existing split view
|
// Add to existing split view
|
||||||
const groupIndex = this._data.findIndex((group) => group.tabs.includes(droppedOnTab));
|
const groupIndex = this._data.findIndex((group) => group.tabs.includes(droppedOnTab));
|
||||||
const group = this._data[groupIndex];
|
const group = this._data[groupIndex];
|
||||||
|
|
||||||
if (!group.tabs.includes(draggedTab) && group.tabs.length < this.MAX_TABS) {
|
if (!group.tabs.includes(draggedTab) && group.tabs.length < this.MAX_TABS) {
|
||||||
// First move the tab to the split view group
|
// First move the tab to the split view group
|
||||||
let splitGroup = droppedOnTab.group;
|
let splitGroup = droppedOnTab.group;
|
||||||
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
|
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
|
||||||
this._moveTabsToContainer([draggedTab], droppedOnTab);
|
this._moveTabsToContainer([draggedTab], droppedOnTab);
|
||||||
gBrowser.moveTabToGroup(draggedTab, splitGroup);
|
gBrowser.moveTabToGroup(draggedTab, splitGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
||||||
const parentNode = droppedOnSplitNode.parent;
|
const parentNode = droppedOnSplitNode.parent;
|
||||||
|
|
||||||
// Then add the tab to the split view
|
// Then add the tab to the split view
|
||||||
group.tabs.push(draggedTab);
|
group.tabs.push(draggedTab);
|
||||||
|
|
||||||
// If dropping on a side, create a new split in that direction
|
// If dropping on a side, create a new split in that direction
|
||||||
if (hoverSide !== 'center') {
|
if (hoverSide !== 'center') {
|
||||||
const splitDirection = hoverSide === 'left' || hoverSide === 'right' ? 'row' : 'column';
|
const splitDirection = hoverSide === 'left' || hoverSide === 'right' ? 'row' : 'column';
|
||||||
if (parentNode.direction !== splitDirection) {
|
if (parentNode.direction !== splitDirection) {
|
||||||
this.splitIntoNode(droppedOnSplitNode, new SplitLeafNode(draggedTab, 50), hoverSide, 0.5);
|
this.splitIntoNode(droppedOnSplitNode, new SplitLeafNode(draggedTab, 50), hoverSide, 0.5);
|
||||||
} else {
|
|
||||||
this.addTabToSplit(draggedTab, parentNode);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.addTabToSplit(draggedTab, group.layoutTree);
|
this.addTabToSplit(draggedTab, parentNode);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
this.activateSplitView(group, true);
|
this.addTabToSplit(draggedTab, group.layoutTree);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Create new split view with layout based on drop position
|
|
||||||
let gridType = 'vsep';
|
|
||||||
//switch (hoverSide) {
|
|
||||||
// case 'left':
|
|
||||||
// case 'right':
|
|
||||||
// gridType = 'vsep';
|
|
||||||
// break;
|
|
||||||
// case 'top':
|
|
||||||
// case 'bottom':
|
|
||||||
// gridType = 'hsep';
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// gridType = 'grid';
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Put tabs always as if it was dropped from the left
|
this.activateSplitView(group, true);
|
||||||
this.splitTabs([draggedTab, droppedOnTab], gridType, 1);
|
|
||||||
if (draggedTab.linkedBrowser) {
|
|
||||||
gZenUIManager.motion.animate(
|
|
||||||
draggedTab.linkedBrowser.closest('.browserSidebarContainer'),
|
|
||||||
{
|
|
||||||
scale: [0.98, 1],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0.5,
|
|
||||||
duration: 0.5,
|
|
||||||
delay: 0.1,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Create new split view with layout based on drop position
|
||||||
|
let gridType = 'vsep';
|
||||||
|
//switch (hoverSide) {
|
||||||
|
// case 'left':
|
||||||
|
// case 'right':
|
||||||
|
// gridType = 'vsep';
|
||||||
|
// break;
|
||||||
|
// case 'top':
|
||||||
|
// case 'bottom':
|
||||||
|
// gridType = 'hsep';
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// gridType = 'grid';
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Put tabs always as if it was dropped from the left
|
||||||
|
this.splitTabs([draggedTab, droppedOnTab], gridType, 1);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
this._maybeRemoveFakeBrowser(false);
|
||||||
|
|
||||||
|
if (browserContainer) {
|
||||||
|
gZenUIManager.motion.animate(
|
||||||
|
browserContainer,
|
||||||
|
{
|
||||||
|
scale: [0.97, 1],
|
||||||
|
opacity: [0, 1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'spring',
|
||||||
|
bounce: 0.4,
|
||||||
|
duration: 0.2,
|
||||||
|
delay: 0.1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user