Implement drag-and-drop functionality for split view and refine CSS styles for better layout and visibility

This commit is contained in:
mr. M
2025-03-02 16:45:57 +01:00
parent e065ccb7b7
commit d415bde66c
6 changed files with 177 additions and 24 deletions

View File

@@ -1,8 +1,8 @@
diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml
index e5f3424eaeeec0ba552537f167dd99e912216d94..4bdfcdb23fe9c44ad3d4de273c64f4cc31cb4034 100644
index 959c523b21c642f29353b9de37b3ce6b5505b01b..0d151ad345dde47467432196ed76f4320b4b92cc 100644
--- a/browser/base/content/main-popupset.inc.xhtml
+++ b/browser/base/content/main-popupset.inc.xhtml
@@ -181,6 +181,10 @@
@@ -206,6 +206,10 @@
hidden="true"
tabspecific="true"
aria-labelledby="editBookmarkPanelTitle">
@@ -13,7 +13,7 @@ index e5f3424eaeeec0ba552537f167dd99e912216d94..4bdfcdb23fe9c44ad3d4de273c64f4cc
<box class="panel-header">
<html:h1>
<html:span id="editBookmarkPanelTitle"/>
@@ -206,6 +210,7 @@
@@ -231,6 +235,7 @@
class="footer-button"/>
</html:moz-button-group>
</vbox>
@@ -21,7 +21,7 @@ index e5f3424eaeeec0ba552537f167dd99e912216d94..4bdfcdb23fe9c44ad3d4de273c64f4cc
</panel>
</html:template>
@@ -535,6 +540,8 @@
@@ -565,6 +570,8 @@
#include popup-notifications.inc.xhtml

View File

@@ -248,3 +248,34 @@
outline-color: var(--toolbarbutton-active-outline-color);
}
}
#zen-split-view-fake-browser {
position: absolute;
height: 100%;
background: rgba(255, 255, 255, 0.1);
border-radius: var(--zen-native-inner-radius);
box-shadow: var(--zen-big-shadow);
pointer-events: none;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 3.5rem;
height: 3.5rem;
background: var(--zen-split-view-fake-icon);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
opacity: 0.8;
transition: opacity 0.2s;
transition-delay: 0.1s;
@starting-style {
opacity: 0;
}
}
}

View File

@@ -9,6 +9,7 @@
justify-content: center;
align-items: center;
display: flex;
font-size: x-small;
position: relative;

View File

@@ -102,7 +102,7 @@ class ZenBrowserManagerSidebar extends ZenDOMOperatedFeature {
syncPinnedState() {
const sidebar = document.getElementById('zen-sidebar-web-panel');
const pinButton = document.getElementById('zen-sidebar-web-panel-pinned');
if (sidebar.hasAttribute('pinned')) {
pinButton.setAttribute('pinned', 'true');
} else {

View File

@@ -84,6 +84,10 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
this.initializeContextMenu();
this.insertPageActionButton();
this.insertIntoContextMenu();
// Add drag over listener to the browser view
this.tabBrowserPanel.addEventListener('dragenter', this.onBrowserDragOverToSplit.bind(this));
this.tabBrowserPanel.addEventListener('dragleave', this.onBrowserDragEndToSplit.bind(this));
}
insertIntoContextMenu() {
@@ -142,6 +146,111 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
}
}
onBrowserDragOverToSplit(event) {
var dt = event.dataTransfer;
var draggedTab;
if (dt.mozTypesAt(0)[0] == TAB_DROP_TYPE) {
// tab copy or move
draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
// not our drop then
if (!draggedTab || gBrowser.selectedTab.hasAttribute('zen-empty-tab')) {
return;
}
draggedTab.container._finishMoveTogetherSelectedTabs(draggedTab);
}
if (!draggedTab || this._canDrop || this._hasAnimated || this.fakeBrowser) {
return;
}
const currentView = this._data[this.currentView];
if (currentView?.tabs.length >= this.MAX_TABS) {
return;
}
this._canDrop = true;
// wait some time before showing the split view
this._showSplitViewTimeout = setTimeout(() => {
this._hasAnimated = true;
const panelsWidth = gBrowser.tabbox.getBoundingClientRect().width;
const halfWidth = panelsWidth / 2;
this.fakeBrowser = document.createXULElement('vbox');
const padding = Services.prefs.getIntPref('zen.theme.content-element-separation', 0);
this.fakeBrowser.setAttribute('flex', '1');
this.fakeBrowser.id = 'zen-split-view-fake-browser';
gBrowser.tabbox.appendChild(this.fakeBrowser);
this.fakeBrowser.style.setProperty('--zen-split-view-fake-icon', `url(${draggedTab.getAttribute('image')})`);
Promise.all([
gZenUIManager.motion.animate(
gBrowser.tabbox,
{
paddingLeft: [0, `${halfWidth}px`],
},
{
duration: 0.1,
easing: 'ease-out',
}
),
gZenUIManager.motion.animate(
this.fakeBrowser,
{
width: [0, `${halfWidth - padding * 2}px`],
marginLeft: [0, `${-(halfWidth - padding)}px`],
},
{
duration: 0.1,
easing: 'ease-out',
}
),
]).then(() => {});
}, 100);
}
onBrowserDragEndToSplit(event) {
if (!this._canDrop) {
return;
}
const panelsRect = gBrowser.tabbox.getBoundingClientRect();
// this event is fired even though we are still in the "allowed" area
if (event.target !== this.tabBrowserPanel) {
return;
}
this._canDrop = false;
if (this._showSplitViewTimeout) {
clearTimeout(this._showSplitViewTimeout);
}
if (!this._hasAnimated) {
return;
}
setTimeout(() => {
const panelsWidth = panelsRect.width;
const halfWidth = panelsWidth / 2;
const padding = Services.prefs.getIntPref('zen.theme.content-element-separation', 0);
Promise.all([
gZenUIManager.motion.animate(
gBrowser.tabbox,
{
paddingLeft: [`${halfWidth}px`, 0],
},
{
duration: 0.1,
easing: 'ease-out',
}
),
gZenUIManager.motion.animate(
this.fakeBrowser,
{
width: [`${halfWidth - padding * 2}px`, 0],
marginLeft: [`${-(halfWidth - padding)}px`, 0],
},
{
duration: 0.1,
easing: 'ease-out',
}
),
]).then(() => {
this._mayabeRemoveFakeBrowser();
});
}, 100);
}
/**
* Remove a SplitNode from its tree and the view
* @param {SplitNode} toRemove
@@ -699,6 +808,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
this.updateSplitView(tab);
tab.linkedBrowser.docShellIsActive = true;
}
this._mayabeRemoveFakeBrowser();
}
/**
@@ -1282,6 +1392,16 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
}
};
_mayabeRemoveFakeBrowser() {
if (this.fakeBrowser) {
this.fakeBrowser.remove();
this.fakeBrowser = null;
gBrowser.tabbox.removeAttribute('style');
delete this._canDrop;
delete this._hasAnimated;
}
}
/**
* @description moves the tab to the split view if dragged on a browser
* @param event - The event
@@ -1289,6 +1409,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
* @returns {boolean} true if the tab was moved to the split view
*/
moveTabToSplitView(event, draggedTab) {
this._mayabeRemoveFakeBrowser();
const dropTarget = document.elementFromPoint(event.clientX, event.clientY);
const browser = dropTarget?.closest('browser');
@@ -1336,24 +1458,22 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
}
} 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';
}
let gridType = 'vsep';
//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);
// Put tabs always as if it was dropped from the left
this.splitTabs([draggedTab, droppedOnTab], gridType);
}
}
return true;

View File

@@ -1140,6 +1140,7 @@ menupopup > menuitem:is([type='checkbox']) .menu-iconic-left {
}
#toolbar-context-toggle-vertical-tabs,
#toolbar-context-customize-sidebar {
display: none;
#toolbar-context-customize-sidebar,
#sidebarRevampSeparator {
display: none !important;
}