Refactor ZenViewSplitter to improve drag-and-drop functionality and enhance split view behavior

This commit is contained in:
mr. M
2025-03-04 20:23:21 +01:00
parent cc8a5cf367
commit 452c19a7ea
3 changed files with 94 additions and 90 deletions

View File

@@ -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() {

View File

@@ -312,9 +312,6 @@ var gZenCompactModeManager = {
{ {
element: gZenVerticalTabsManager.actualWindowButtons, element: gZenVerticalTabsManager.actualWindowButtons,
}, },
{
element: gZenVerticalTabsManager.actualWindowButtons,
},
]; ];
}, },

View File

@@ -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;
} }