From 73dc9e79c257172d869bfbb115da3c8d2684c874 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sat, 14 Sep 2024 15:33:23 +0200 Subject: [PATCH 01/11] Resize views in split view (only works in vertical, still has problems) --- src/ZenViewSplitter.mjs | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 1bac78d..0703d78 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -270,6 +270,7 @@ var gZenViewSplitter = new (class { } this.activateSplitView(splitData, tab); + if (!splitData.sizes) this.applySplitters(splitData); } /** @@ -322,6 +323,27 @@ var gZenViewSplitter = new (class { }); } + applySplitters(splitData) { + const tabs = splitData.tabs; + for (let i = 0; i < tabs.length; i++) { + const tab = tabs[0]; + const container = tab.linkedBrowser.closest('.browserSidebarContainer'); + + if (splitData.gridType === 'vsep' && i != tabs.length - 1) { + let splitter = document.createXULElement('div'); + splitter.className = 'zen-split-view-splitter'; + splitter.setAttribute('orient', 'vertical'); + splitter.addEventListener('mousedown', this.handleSplitterMouseDown); + container.insertAdjacentElement("beforeend", splitter); + } + } + + if (splitData.gridType === 'vsep') { + const w = 100 / tabs.length; + splitData.sizes = tabs.map(t => ({width: w})); + } + } + /** * Calculates the grid areas for the tabs. * @@ -401,6 +423,32 @@ var gZenViewSplitter = new (class { } }; + handleSplitterMouseDown = (event) => { + const container = event.target.parentElement; + const tab = window.gBrowser.tabs.find((t) => t.linkedBrowser.closest('.browserSidebarContainer') === container); + const currentView = this._data[this.currentView]; + + const tabIdx = currentView.tabs.findIndex(t => t === tab); + let dragFunc; + let prevX = event.clientX; + dragFunc = (dEvent) => { + requestAnimationFrame(() => { + const movementX = dEvent.clientX - prevX; + const percentageChange = (movementX / this._tabBrowserPanel.getBoundingClientRect().width) * 100; + currentView.sizes[tabIdx].width += percentageChange; + currentView.sizes[tabIdx + 1].width -= percentageChange; + this._tabBrowserPanel.style['grid-template-columns'] = currentView.tabs.map((t, i) => `${currentView.sizes[i].width}%`).join(' '); + prevX = dEvent.clientX; + }); + } + const stopListeners = () => { + removeEventListener('mousemove', dragFunc); + removeEventListener('mouseup', stopListeners); + } + addEventListener('mousemove', dragFunc); + addEventListener('mouseup', stopListeners); + } + /** * Sets the docshell state for the tabs. * From 0daa12f76264e23fab71a461e94aaa77ae6207c2 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:19:07 +0200 Subject: [PATCH 02/11] Put resize splitters inside tabBrowser --- src/ZenViewSplitter.mjs | 54 ++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 0703d78..00347de 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -269,8 +269,8 @@ var gZenViewSplitter = new (class { } } + this.applySplitters(splitData); this.activateSplitView(splitData, tab); - if (!splitData.sizes) this.applySplitters(splitData); } /** @@ -284,6 +284,7 @@ var gZenViewSplitter = new (class { } this.tabBrowserPanel.removeAttribute('zen-split-view'); this.tabBrowserPanel.style.gridTemplateAreas = ''; + this.tabBrowserPanel.style.gridTemplateColumns = ''; this.setTabsDocShellState(this._data[this.currentView].tabs, false); this.currentView = -1; } @@ -303,6 +304,7 @@ var gZenViewSplitter = new (class { this.setTabsDocShellState(splitData.tabs, true); this.updateSplitViewButton(false); + this.updateGridSizes(); } /** @@ -325,22 +327,27 @@ var gZenViewSplitter = new (class { applySplitters(splitData) { const tabs = splitData.tabs; - for (let i = 0; i < tabs.length; i++) { - const tab = tabs[0]; - const container = tab.linkedBrowser.closest('.browserSidebarContainer'); + const gridType = splitData.gridType; - if (splitData.gridType === 'vsep' && i != tabs.length - 1) { - let splitter = document.createXULElement('div'); - splitter.className = 'zen-split-view-splitter'; - splitter.setAttribute('orient', 'vertical'); - splitter.addEventListener('mousedown', this.handleSplitterMouseDown); - container.insertAdjacentElement("beforeend", splitter); - } + const vSplitters = gridType === 'vsep' ? (tabs.length - 1) : gridType === 'hsep' ? 0 : NaN; + + const totalVsplitters = Math.max(...this._data.map(s => s.verticalSplitters || 0)); + for (let i = totalVsplitters; i < vSplitters; i++) { + let splitter = document.createXULElement('div'); + splitter.className = 'zen-split-view-splitter'; + splitter.setAttribute('orient', 'vertical'); + splitter.setAttribute('idx', i + 1); + splitter.style = `grid-area: splitter${i + 1}`; + splitter.addEventListener('mousedown', this.handleSplitterMouseDown); + this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); } + splitData.verticalSplitters = vSplitters; - if (splitData.gridType === 'vsep') { - const w = 100 / tabs.length; - splitData.sizes = tabs.map(t => ({width: w})); + if (!splitData.sizes) { + if (splitData.gridType === 'vsep') { + const w = 100 / tabs.length; + splitData.sizes = tabs.map(t => ({width: w})); + } } } @@ -356,7 +363,7 @@ var gZenViewSplitter = new (class { return this.calculateGridAreasForGrid(tabs); } if (gridType === 'vsep') { - return `'${tabs.map((_, j) => `tab${j + 1}`).join(' ')}'`; + return `'${tabs.slice(0, -1).map((_, j) => `tab${j + 1} splitter${j + 1}`).join(' ')} tab${tabs.length}'`; } if (gridType === 'hsep') { return tabs.map((_, j) => `'tab${j + 1}'`).join(' '); @@ -428,16 +435,16 @@ var gZenViewSplitter = new (class { const tab = window.gBrowser.tabs.find((t) => t.linkedBrowser.closest('.browserSidebarContainer') === container); const currentView = this._data[this.currentView]; - const tabIdx = currentView.tabs.findIndex(t => t === tab); + const tabIdx = event.target.getAttribute('idx'); let dragFunc; let prevX = event.clientX; dragFunc = (dEvent) => { requestAnimationFrame(() => { const movementX = dEvent.clientX - prevX; const percentageChange = (movementX / this._tabBrowserPanel.getBoundingClientRect().width) * 100; - currentView.sizes[tabIdx].width += percentageChange; - currentView.sizes[tabIdx + 1].width -= percentageChange; - this._tabBrowserPanel.style['grid-template-columns'] = currentView.tabs.map((t, i) => `${currentView.sizes[i].width}%`).join(' '); + currentView.sizes[tabIdx - 1].width += percentageChange; + currentView.sizes[tabIdx].width -= percentageChange; + this.updateGridSizes(); prevX = dEvent.clientX; }); } @@ -449,6 +456,13 @@ var gZenViewSplitter = new (class { addEventListener('mouseup', stopListeners); } + updateGridSizes() { + const currentView = this._data[this.currentView]; + this._tabBrowserPanel.style.gridTemplateColumns = currentView.tabs.slice(0, -1).map( + (t, i) => `calc(${currentView.sizes[i].width}% - 7px) 7px` + ).join(' '); + } + /** * Sets the docshell state for the tabs. * @@ -631,4 +645,4 @@ var gZenViewSplitter = new (class { : [gBrowser.selectedTab, tabs[nextTabIndex]]; this.splitTabs(selected_tabs, gridType); } -})(); +})(); \ No newline at end of file From 88b9459f30306c9ca80161a69686a356e92cd782 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 15 Sep 2024 10:43:37 +0200 Subject: [PATCH 03/11] Add minimal width when resizing split view. Remove excess splitters on apply. --- src/ZenViewSplitter.mjs | 53 ++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 00347de..8881a05 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -3,6 +3,7 @@ var gZenViewSplitter = new (class { this._data = []; this.currentView = -1; this._tabBrowserPanel = null; + this._minAdjustmentWidth = 7; this.__modifierElement = null; this.__hasSetMenuListener = false; @@ -329,19 +330,24 @@ var gZenViewSplitter = new (class { const tabs = splitData.tabs; const gridType = splitData.gridType; - const vSplitters = gridType === 'vsep' ? (tabs.length - 1) : gridType === 'hsep' ? 0 : NaN; - - const totalVsplitters = Math.max(...this._data.map(s => s.verticalSplitters || 0)); - for (let i = totalVsplitters; i < vSplitters; i++) { + const insertSplitter = (i, orient) => { let splitter = document.createXULElement('div'); splitter.className = 'zen-split-view-splitter'; - splitter.setAttribute('orient', 'vertical'); + splitter.setAttribute('orient', orient); splitter.setAttribute('idx', i + 1); splitter.style = `grid-area: splitter${i + 1}`; splitter.addEventListener('mousedown', this.handleSplitterMouseDown); this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); } - splitData.verticalSplitters = vSplitters; + + const vSplittersNeeded = gridType === 'vsep' ? (tabs.length - 1) : gridType === 'hsep' ? 0 : NaN; + const vSplitters = [...gZenViewSplitter.tabBrowserPanel.children].filter(e => e.classList.contains('zen-split-view-splitter')); + for (let i = vSplitters.length; i < vSplittersNeeded; i++) { + insertSplitter(i, 'vertical'); + } + for (let i = 0; i < vSplitters.length - vSplittersNeeded; i++) { + vSplitters[i].remove(); + } if (!splitData.sizes) { if (splitData.gridType === 'vsep') { @@ -431,21 +437,36 @@ var gZenViewSplitter = new (class { }; handleSplitterMouseDown = (event) => { - const container = event.target.parentElement; - const tab = window.gBrowser.tabs.find((t) => t.linkedBrowser.closest('.browserSidebarContainer') === container); const currentView = this._data[this.currentView]; + const isVertical = event.target.getAttribute('orient') === 'vertical'; + const dimension = isVertical ? 'width' : 'height'; + const clientAxis = isVertical ? 'clientX' : 'clientY'; + const tabIdx = event.target.getAttribute('idx'); - let dragFunc; - let prevX = event.clientX; - dragFunc = (dEvent) => { + let prevPosition = event[clientAxis]; + const dragFunc = (dEvent) => { requestAnimationFrame(() => { - const movementX = dEvent.clientX - prevX; - const percentageChange = (movementX / this._tabBrowserPanel.getBoundingClientRect().width) * 100; - currentView.sizes[tabIdx - 1].width += percentageChange; - currentView.sizes[tabIdx].width -= percentageChange; + const movementX = dEvent[clientAxis] - prevPosition; + let percentageChange = (movementX / this._tabBrowserPanel.getBoundingClientRect()[dimension]) * 100; + + const currentSize = currentView.sizes[tabIdx - 1][dimension]; + const neighborSize = currentView.sizes[tabIdx][dimension]; + if (currentSize < this._minAdjustmentWidth && neighborSize < this._minAdjustmentWidth) { + return; + } + let max = false; + if (currentSize + percentageChange < this._minAdjustmentWidth) { + percentageChange = this._minAdjustmentWidth - currentSize; + max = true; + } else if (neighborSize - percentageChange < this._minAdjustmentWidth) { + percentageChange = neighborSize - this._minAdjustmentWidth; + max = true; + } + currentView.sizes[tabIdx - 1][dimension] += percentageChange; + currentView.sizes[tabIdx][dimension] -= percentageChange; this.updateGridSizes(); - prevX = dEvent.clientX; + if (!max) prevPosition = dEvent.clientX; }); } const stopListeners = () => { From e4cb4646b8044ec4b5bf1b60a68bc3f023bcf464 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 15 Sep 2024 11:51:01 +0200 Subject: [PATCH 04/11] Make split view resizing work for horizontal split. --- src/ZenViewSplitter.mjs | 48 +++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 8881a05..ac87a7a 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -340,8 +340,11 @@ var gZenViewSplitter = new (class { this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); } + + const splitters = [...gZenViewSplitter.tabBrowserPanel.children].filter(e => e.classList.contains('zen-split-view-splitter')); + const vSplittersNeeded = gridType === 'vsep' ? (tabs.length - 1) : gridType === 'hsep' ? 0 : NaN; - const vSplitters = [...gZenViewSplitter.tabBrowserPanel.children].filter(e => e.classList.contains('zen-split-view-splitter')); + const vSplitters = splitters.filter(s => s.getAttribute('orient') === 'vertical'); for (let i = vSplitters.length; i < vSplittersNeeded; i++) { insertSplitter(i, 'vertical'); } @@ -349,10 +352,23 @@ var gZenViewSplitter = new (class { vSplitters[i].remove(); } + const hSplittersNeeded = gridType === 'vsep' ? 0 : gridType === 'hsep' ? (tabs.length - 1) : NaN; + const hSplitters = splitters.filter(s => s.getAttribute('orient') === 'horizontal'); + for (let i = hSplitters.length; i < hSplittersNeeded; i++) { + insertSplitter(i, 'horizontal'); + } + for (let i = 0; i < hSplitters.length - hSplittersNeeded; i++) { + vSplitters[i].remove(); + } + if (!splitData.sizes) { if (splitData.gridType === 'vsep') { const w = 100 / tabs.length; - splitData.sizes = tabs.map(t => ({width: w})); + splitData.widths = tabs.map(t => w); + splitData.heights = [100]; + } else if (splitData.gridType === 'hsep') { + splitData.widths = [100]; + splitData.heights = tabs.map(t => 100 / tabs.length); } } } @@ -372,7 +388,7 @@ var gZenViewSplitter = new (class { return `'${tabs.slice(0, -1).map((_, j) => `tab${j + 1} splitter${j + 1}`).join(' ')} tab${tabs.length}'`; } if (gridType === 'hsep') { - return tabs.map((_, j) => `'tab${j + 1}'`).join(' '); + return tabs.slice(0, -1).map((_, j) => `'tab${j + 1}' 'splitter${j + 1}'`).join(' ') + `'tab${tabs.length}`; } return ''; } @@ -437,10 +453,10 @@ var gZenViewSplitter = new (class { }; handleSplitterMouseDown = (event) => { - const currentView = this._data[this.currentView]; + const splitData = this._data[this.currentView]; const isVertical = event.target.getAttribute('orient') === 'vertical'; - const dimension = isVertical ? 'width' : 'height'; + const dimension = isVertical ? 'widths' : 'heights'; const clientAxis = isVertical ? 'clientX' : 'clientY'; const tabIdx = event.target.getAttribute('idx'); @@ -448,10 +464,10 @@ var gZenViewSplitter = new (class { const dragFunc = (dEvent) => { requestAnimationFrame(() => { const movementX = dEvent[clientAxis] - prevPosition; - let percentageChange = (movementX / this._tabBrowserPanel.getBoundingClientRect()[dimension]) * 100; + let percentageChange = (movementX / this.tabBrowserPanel.getBoundingClientRect()[isVertical ? 'width' : 'height']) * 100; - const currentSize = currentView.sizes[tabIdx - 1][dimension]; - const neighborSize = currentView.sizes[tabIdx][dimension]; + const currentSize = splitData[dimension][tabIdx - 1]; + const neighborSize = splitData[dimension][tabIdx]; if (currentSize < this._minAdjustmentWidth && neighborSize < this._minAdjustmentWidth) { return; } @@ -463,10 +479,10 @@ var gZenViewSplitter = new (class { percentageChange = neighborSize - this._minAdjustmentWidth; max = true; } - currentView.sizes[tabIdx - 1][dimension] += percentageChange; - currentView.sizes[tabIdx][dimension] -= percentageChange; + splitData[dimension][tabIdx - 1] += percentageChange; + splitData[dimension][tabIdx] -= percentageChange; this.updateGridSizes(); - if (!max) prevPosition = dEvent.clientX; + if (!max) prevPosition = dEvent[clientAxis]; }); } const stopListeners = () => { @@ -478,9 +494,13 @@ var gZenViewSplitter = new (class { } updateGridSizes() { - const currentView = this._data[this.currentView]; - this._tabBrowserPanel.style.gridTemplateColumns = currentView.tabs.slice(0, -1).map( - (t, i) => `calc(${currentView.sizes[i].width}% - 7px) 7px` + const splitData = this._data[this.currentView]; + this.tabBrowserPanel.style.gridTemplateColumns = splitData.tabs.slice(0, -1).map( + (t, i) => `calc(${splitData.widths[i]}% - 7px) 7px` + ).join(' '); + + this.tabBrowserPanel.style.gridTemplateRows = splitData.tabs.slice(0, -1).map( + (t, i) => `calc(${splitData.heights[i]}% - 7px) 7px` ).join(' '); } From ad106d5337449fe26c2f1dde3e68844525bf6e09 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 15 Sep 2024 13:08:11 +0200 Subject: [PATCH 05/11] Fix deactivate split view. --- src/ZenViewSplitter.mjs | 49 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index ac87a7a..ce5c5f6 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -102,6 +102,8 @@ var gZenViewSplitter = new (class { this.currentView = -1; this.tabBrowserPanel.removeAttribute('zen-split-view'); this.tabBrowserPanel.style.gridTemplateAreas = ''; + this.tabBrowserPanel.style.gridTemplateColumns = ''; + this.tabBrowserPanel.style.gridTemplateRows = ''; } /** @@ -270,7 +272,6 @@ var gZenViewSplitter = new (class { } } - this.applySplitters(splitData); this.activateSplitView(splitData, tab); } @@ -286,6 +287,7 @@ var gZenViewSplitter = new (class { this.tabBrowserPanel.removeAttribute('zen-split-view'); this.tabBrowserPanel.style.gridTemplateAreas = ''; this.tabBrowserPanel.style.gridTemplateColumns = ''; + this.tabBrowserPanel.style.gridTemplateRows = ''; this.setTabsDocShellState(this._data[this.currentView].tabs, false); this.currentView = -1; } @@ -305,6 +307,7 @@ var gZenViewSplitter = new (class { this.setTabsDocShellState(splitData.tabs, true); this.updateSplitViewButton(false); + this.applySplitters(splitData); this.updateGridSizes(); } @@ -335,12 +338,11 @@ var gZenViewSplitter = new (class { splitter.className = 'zen-split-view-splitter'; splitter.setAttribute('orient', orient); splitter.setAttribute('idx', i + 1); - splitter.style = `grid-area: splitter${i + 1}`; + splitter.style = `grid-area: ${orient === 'vertical' ? 'v' : 'h'}Splitter${i + 1}`; splitter.addEventListener('mousedown', this.handleSplitterMouseDown); this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); } - const splitters = [...gZenViewSplitter.tabBrowserPanel.children].filter(e => e.classList.contains('zen-split-view-splitter')); const vSplittersNeeded = gridType === 'vsep' ? (tabs.length - 1) : gridType === 'hsep' ? 0 : NaN; @@ -358,18 +360,15 @@ var gZenViewSplitter = new (class { insertSplitter(i, 'horizontal'); } for (let i = 0; i < hSplitters.length - hSplittersNeeded; i++) { - vSplitters[i].remove(); + hSplitters[i].remove(); } - if (!splitData.sizes) { - if (splitData.gridType === 'vsep') { - const w = 100 / tabs.length; - splitData.widths = tabs.map(t => w); - splitData.heights = [100]; - } else if (splitData.gridType === 'hsep') { - splitData.widths = [100]; - splitData.heights = tabs.map(t => 100 / tabs.length); - } + if (splitData.gridType === 'vsep' && splitData?.widths !== splitData.tabs.length) { + splitData.widths = tabs.map(t => 100 / tabs.length); + splitData.heights = [100]; + } else if (splitData.gridType === 'hsep' && splitData?.widths !== splitData.tabs.length) { + splitData.widths = [100]; + splitData.heights = tabs.map(t => 100 / tabs.length); } } @@ -385,10 +384,10 @@ var gZenViewSplitter = new (class { return this.calculateGridAreasForGrid(tabs); } if (gridType === 'vsep') { - return `'${tabs.slice(0, -1).map((_, j) => `tab${j + 1} splitter${j + 1}`).join(' ')} tab${tabs.length}'`; + return `'${tabs.slice(0, -1).map((_, j) => `tab${j + 1} vSplitter${j + 1}`).join(' ')} tab${tabs.length}'`; } if (gridType === 'hsep') { - return tabs.slice(0, -1).map((_, j) => `'tab${j + 1}' 'splitter${j + 1}'`).join(' ') + `'tab${tabs.length}`; + return tabs.slice(0, -1).map((_, j) => `'tab${j + 1}' 'hSplitter${j + 1}'`).join(' ') + `'tab${tabs.length}`; } return ''; } @@ -495,13 +494,21 @@ var gZenViewSplitter = new (class { updateGridSizes() { const splitData = this._data[this.currentView]; - this.tabBrowserPanel.style.gridTemplateColumns = splitData.tabs.slice(0, -1).map( - (t, i) => `calc(${splitData.widths[i]}% - 7px) 7px` - ).join(' '); + if (splitData.widths.length === 1) { + this.tabBrowserPanel.style.gridTemplateColumns = ''; + } else { + this.tabBrowserPanel.style.gridTemplateColumns = splitData.widths.slice(0, -1).map( + (w) => `calc(${w}% - 7px) 7px` + ).join(' '); + } - this.tabBrowserPanel.style.gridTemplateRows = splitData.tabs.slice(0, -1).map( - (t, i) => `calc(${splitData.heights[i]}% - 7px) 7px` - ).join(' '); + if (splitData.heights.length === 1) { + this.tabBrowserPanel.style.gridTemplateRows = ''; + } else { + this.tabBrowserPanel.style.gridTemplateRows = splitData.heights.slice(0, -1).map( + (h) => `calc(${h}% - 7px) 7px` + ).join(' '); + } } /** From 44f524523bcb324984d409035c9cb50bb8d39a01 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 15 Sep 2024 17:43:23 +0200 Subject: [PATCH 06/11] Make split view resize work for grid mode --- src/ZenViewSplitter.mjs | 98 ++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index ce5c5f6..4178d91 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -337,38 +337,58 @@ var gZenViewSplitter = new (class { let splitter = document.createXULElement('div'); splitter.className = 'zen-split-view-splitter'; splitter.setAttribute('orient', orient); - splitter.setAttribute('idx', i + 1); - splitter.style = `grid-area: ${orient === 'vertical' ? 'v' : 'h'}Splitter${i + 1}`; + splitter.setAttribute('idx', gridType === 'grid' && orient === 'vertical' ? Math.ceil(i/2) : i); + splitter.style = `grid-area: ${orient === 'vertical' ? 'v' : 'h'}Splitter${i}`; splitter.addEventListener('mousedown', this.handleSplitterMouseDown); this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); } - const splitters = [...gZenViewSplitter.tabBrowserPanel.children].filter(e => e.classList.contains('zen-split-view-splitter')); - const vSplittersNeeded = gridType === 'vsep' ? (tabs.length - 1) : gridType === 'hsep' ? 0 : NaN; - const vSplitters = splitters.filter(s => s.getAttribute('orient') === 'vertical'); - for (let i = vSplitters.length; i < vSplittersNeeded; i++) { + let vSplittersNeeded; let hSplittersNeeded; + switch (gridType) { + case 'vsep': + vSplittersNeeded = tabs.length - 1; + hSplittersNeeded = 0; + break; + case 'hsep': + vSplittersNeeded = 0 + hSplittersNeeded = tabs.length - 1; + break; + case 'grid': + if (tabs.length < 2) { + vSplittersNeeded = 1; + hSplittersNeeded = 0; + } else { + vSplittersNeeded = (tabs.length - ((tabs.length % 2 === 0) ? 2 : 1)); + hSplittersNeeded = 1; + } + } + + + const splitters = [...gZenViewSplitter.tabBrowserPanel.children].filter(e => e.classList.contains('zen-split-view-splitter')); + splitters.forEach(s => s.remove()); + + for (let i = 1; i <= vSplittersNeeded; i++) { insertSplitter(i, 'vertical'); } - for (let i = 0; i < vSplitters.length - vSplittersNeeded; i++) { - vSplitters[i].remove(); + + for (let i = 1; i <= hSplittersNeeded; i++) { + insertSplitter(i, 'horizontal'); } - const hSplittersNeeded = gridType === 'vsep' ? 0 : gridType === 'hsep' ? (tabs.length - 1) : NaN; - const hSplitters = splitters.filter(s => s.getAttribute('orient') === 'horizontal'); - for (let i = hSplitters.length; i < hSplittersNeeded; i++) { - insertSplitter(i, 'horizontal'); + let nrOfWidths = 1; + let nrOfHeights = 1; + if (splitData.gridType === 'vsep') { + nrOfWidths = tabs.length; + } else if (splitData.gridType === 'hsep') { + nrOfHeights = tabs.length; + } else if (splitData.gridType === 'grid') { + nrOfWidths = tabs.length > 2 ? Math.ceil(tabs.length / 2) : 2; + nrOfHeights = tabs.length > 2 ? 2 : 1; } - for (let i = 0; i < hSplitters.length - hSplittersNeeded; i++) { - hSplitters[i].remove(); - } - - if (splitData.gridType === 'vsep' && splitData?.widths !== splitData.tabs.length) { - splitData.widths = tabs.map(t => 100 / tabs.length); - splitData.heights = [100]; - } else if (splitData.gridType === 'hsep' && splitData?.widths !== splitData.tabs.length) { - splitData.widths = [100]; - splitData.heights = tabs.map(t => 100 / tabs.length); + if (splitData.widths?.length !== nrOfWidths || splitData.heights?.length !== nrOfHeights) { + splitData.widths = Array(nrOfWidths).fill(100 / nrOfWidths); + splitData.heights = Array(nrOfHeights).fill(100 / nrOfHeights); } } @@ -399,24 +419,32 @@ var gZenViewSplitter = new (class { * @returns {string} The calculated grid areas. */ calculateGridAreasForGrid(tabs) { - const rows = ['', '']; - tabs.forEach((_, i) => { - if (i % 2 === 0) { - rows[0] += ` tab${i + 1}`; - } else { - rows[1] += ` tab${i + 1}`; - } - }); - if (tabs.length === 2) { - return "'tab1 tab2'"; + return "'tab1 vSplitter1 tab2'"; } + const rows = ['', '']; + for (let i = 0; i < tabs.length - 2; i++) { + if (i % 2 === 0) { + rows[0] += ` tab${i + 1} vSplitter${i + 1}`; + } else { + rows[1] += ` tab${i + 1} vSplitter${i + 1}`; + } + } + for (let i = tabs.length - 2; i < tabs.length; i++) { + if (i % 2 === 0) { + rows[0] += ` tab${i + 1}`; + } else { + rows[1] += ` tab${i + 1}`; + } + } + + let middleColumn = 'hSplitter1 '.repeat(tabs.length - 1); if (tabs.length % 2 !== 0) { - rows[1] += ` tab${tabs.length}`; + rows[1] += ` vSplitter${tabs.length - 1} tab${tabs.length}`; + middleColumn += ` tab${tabs.length}`; } - - return `'${rows[0].trim()}' '${rows[1].trim()}'`; + return `'${rows[0].trim()}' '${middleColumn}' '${rows[1].trim()}'`; } /** From 64128671a843cd2710b9081e064e59b427eec9c4 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 15 Sep 2024 19:47:10 +0200 Subject: [PATCH 07/11] Refactor gZenViewSplitter.applySplitters() --- src/ZenViewSplitter.mjs | 89 +++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 4178d91..5a913f2 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -98,6 +98,7 @@ var gZenViewSplitter = new (class { for (const tab of this._data[this.currentView].tabs) { this.resetTabState(tab, true); } + this.removeSplitters(); this.currentView = -1; this.tabBrowserPanel.removeAttribute('zen-split-view'); @@ -329,53 +330,15 @@ var gZenViewSplitter = new (class { }); } + /** + * Adds splitters to tabBrowserPanel, adds default widths and heights to splitData + * + * @param {object} splitData - The split data. + */ applySplitters(splitData) { const tabs = splitData.tabs; const gridType = splitData.gridType; - const insertSplitter = (i, orient) => { - let splitter = document.createXULElement('div'); - splitter.className = 'zen-split-view-splitter'; - splitter.setAttribute('orient', orient); - splitter.setAttribute('idx', gridType === 'grid' && orient === 'vertical' ? Math.ceil(i/2) : i); - splitter.style = `grid-area: ${orient === 'vertical' ? 'v' : 'h'}Splitter${i}`; - splitter.addEventListener('mousedown', this.handleSplitterMouseDown); - this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); - } - - - let vSplittersNeeded; let hSplittersNeeded; - switch (gridType) { - case 'vsep': - vSplittersNeeded = tabs.length - 1; - hSplittersNeeded = 0; - break; - case 'hsep': - vSplittersNeeded = 0 - hSplittersNeeded = tabs.length - 1; - break; - case 'grid': - if (tabs.length < 2) { - vSplittersNeeded = 1; - hSplittersNeeded = 0; - } else { - vSplittersNeeded = (tabs.length - ((tabs.length % 2 === 0) ? 2 : 1)); - hSplittersNeeded = 1; - } - } - - - const splitters = [...gZenViewSplitter.tabBrowserPanel.children].filter(e => e.classList.contains('zen-split-view-splitter')); - splitters.forEach(s => s.remove()); - - for (let i = 1; i <= vSplittersNeeded; i++) { - insertSplitter(i, 'vertical'); - } - - for (let i = 1; i <= hSplittersNeeded; i++) { - insertSplitter(i, 'horizontal'); - } - let nrOfWidths = 1; let nrOfHeights = 1; if (splitData.gridType === 'vsep') { @@ -390,6 +353,36 @@ var gZenViewSplitter = new (class { splitData.widths = Array(nrOfWidths).fill(100 / nrOfWidths); splitData.heights = Array(nrOfHeights).fill(100 / nrOfHeights); } + + let vSplittersNeeded = nrOfWidths - 1; + let hSplittersNeeded = nrOfHeights - 1; + if (splitData.gridType === 'grid' && tabs.length > 2) { + vSplittersNeeded = (tabs.length - ((tabs.length % 2 === 0) ? 2 : 1)); + hSplittersNeeded = 1; + } + this.removeSplitters(); + + const insertSplitter = (i, orient) => { + const splitter = document.createXULElement('div'); + splitter.className = 'zen-split-view-splitter'; + splitter.setAttribute('orient', orient); + splitter.setAttribute('gridIdx', gridType === 'grid' && orient === 'vertical' ? Math.ceil(i/2) : i); + splitter.style = `grid-area: ${orient === 'vertical' ? 'v' : 'h'}Splitter${i}`; + splitter.addEventListener('mousedown', this.handleSplitterMouseDown); + this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); + } + for (let i = 1; i <= vSplittersNeeded; i++) { + insertSplitter(i, 'vertical'); + } + for (let i = 1; i <= hSplittersNeeded; i++) { + insertSplitter(i, 'horizontal'); + } + } + + removeSplitters() { + [...gZenViewSplitter.tabBrowserPanel.children] + .filter(e => e.classList.contains('zen-split-view-splitter')) + .forEach(s => s.remove()); } /** @@ -486,15 +479,15 @@ var gZenViewSplitter = new (class { const dimension = isVertical ? 'widths' : 'heights'; const clientAxis = isVertical ? 'clientX' : 'clientY'; - const tabIdx = event.target.getAttribute('idx'); + const gridIdx = event.target.getAttribute('gridIdx'); let prevPosition = event[clientAxis]; const dragFunc = (dEvent) => { requestAnimationFrame(() => { const movementX = dEvent[clientAxis] - prevPosition; let percentageChange = (movementX / this.tabBrowserPanel.getBoundingClientRect()[isVertical ? 'width' : 'height']) * 100; - const currentSize = splitData[dimension][tabIdx - 1]; - const neighborSize = splitData[dimension][tabIdx]; + const currentSize = splitData[dimension][gridIdx - 1]; + const neighborSize = splitData[dimension][gridIdx]; if (currentSize < this._minAdjustmentWidth && neighborSize < this._minAdjustmentWidth) { return; } @@ -506,8 +499,8 @@ var gZenViewSplitter = new (class { percentageChange = neighborSize - this._minAdjustmentWidth; max = true; } - splitData[dimension][tabIdx - 1] += percentageChange; - splitData[dimension][tabIdx] -= percentageChange; + splitData[dimension][gridIdx - 1] += percentageChange; + splitData[dimension][gridIdx] -= percentageChange; this.updateGridSizes(); if (!max) prevPosition = dEvent[clientAxis]; }); From f29af2b1157c36140d15f6df73295327bda358b2 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Mon, 16 Sep 2024 02:45:22 +0200 Subject: [PATCH 08/11] Fix clientX not consistent on about: pages, use css vars for gridSizes --- src/ZenViewSplitter.mjs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 5a913f2..5a23d4f 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -477,7 +477,7 @@ var gZenViewSplitter = new (class { const isVertical = event.target.getAttribute('orient') === 'vertical'; const dimension = isVertical ? 'widths' : 'heights'; - const clientAxis = isVertical ? 'clientX' : 'clientY'; + const clientAxis = isVertical ? 'screenX' : 'screenY'; const gridIdx = event.target.getAttribute('gridIdx'); let prevPosition = event[clientAxis]; @@ -515,21 +515,16 @@ var gZenViewSplitter = new (class { updateGridSizes() { const splitData = this._data[this.currentView]; - if (splitData.widths.length === 1) { - this.tabBrowserPanel.style.gridTemplateColumns = ''; - } else { - this.tabBrowserPanel.style.gridTemplateColumns = splitData.widths.slice(0, -1).map( - (w) => `calc(${w}% - 7px) 7px` - ).join(' '); - } + const columnGap = 'var(--zen-split-column-gap)'; + const rowGap = 'var(--zen-split-row-gap)'; - if (splitData.heights.length === 1) { - this.tabBrowserPanel.style.gridTemplateRows = ''; - } else { - this.tabBrowserPanel.style.gridTemplateRows = splitData.heights.slice(0, -1).map( - (h) => `calc(${h}% - 7px) 7px` - ).join(' '); - } + this.tabBrowserPanel.style.gridTemplateColumns = splitData.widths.slice(0, -1).map( + (w) => `calc(${w}% - ${columnGap} * ${splitData.widths.length - 1}/${splitData.widths.length}) ${columnGap}` + ).join(' '); + + this.tabBrowserPanel.style.gridTemplateRows = splitData.heights.slice(0, -1).map( + (h) => `calc(${h}% - ${rowGap} * ${splitData.heights.length - 1}/${splitData.heights.length}) ${rowGap}` + ).join(' '); } /** From ce3b03fd77edc7e7111c250b0428af3014c96cbc Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:34:24 +0200 Subject: [PATCH 09/11] Apply cursor resize when resizing in split view. --- src/ZenViewSplitter.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 5a23d4f..e3c6dc9 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -508,9 +508,11 @@ var gZenViewSplitter = new (class { const stopListeners = () => { removeEventListener('mousemove', dragFunc); removeEventListener('mouseup', stopListeners); + setCursor('auto'); } addEventListener('mousemove', dragFunc); addEventListener('mouseup', stopListeners); + setCursor(isVertical ? 'ew-resize' : 'n-resize'); } updateGridSizes() { From 1dc39bed26e6798ecd0a89b7e6a540439ffc44b0 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Mon, 16 Sep 2024 04:09:59 +0200 Subject: [PATCH 10/11] Add zen.splitView.min-resize-width pref --- src/ZenViewSplitter.mjs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index e3c6dc9..6d8776e 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -3,7 +3,6 @@ var gZenViewSplitter = new (class { this._data = []; this.currentView = -1; this._tabBrowserPanel = null; - this._minAdjustmentWidth = 7; this.__modifierElement = null; this.__hasSetMenuListener = false; @@ -172,6 +171,10 @@ var gZenViewSplitter = new (class { return this._tabBrowserPanel; } + get minResizeWidth() { + return Services.prefs.getIntPref('zen.splitView.min-resize-width'); + } + /** * Splits a link in a new tab. */ @@ -488,15 +491,15 @@ var gZenViewSplitter = new (class { const currentSize = splitData[dimension][gridIdx - 1]; const neighborSize = splitData[dimension][gridIdx]; - if (currentSize < this._minAdjustmentWidth && neighborSize < this._minAdjustmentWidth) { + if (currentSize < this.minResizeWidth && neighborSize < this.minResizeWidth) { return; } let max = false; - if (currentSize + percentageChange < this._minAdjustmentWidth) { - percentageChange = this._minAdjustmentWidth - currentSize; + if (currentSize + percentageChange < this.minResizeWidth) { + percentageChange = this.minResizeWidth - currentSize; max = true; - } else if (neighborSize - percentageChange < this._minAdjustmentWidth) { - percentageChange = neighborSize - this._minAdjustmentWidth; + } else if (neighborSize - percentageChange < this.minResizeWidth) { + percentageChange = neighborSize - this.minResizeWidth; max = true; } splitData[dimension][gridIdx - 1] += percentageChange; From 380552d54e33643f6cd1b26d42c1c7889c92200c Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:37:50 +0200 Subject: [PATCH 11/11] Make zenViewSplitter.applySplitters() independent of gridType, extract updateGridSizes from applySplitters --- src/ZenViewSplitter.mjs | 74 +++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/ZenViewSplitter.mjs b/src/ZenViewSplitter.mjs index 6d8776e..d0782a3 100644 --- a/src/ZenViewSplitter.mjs +++ b/src/ZenViewSplitter.mjs @@ -311,8 +311,9 @@ var gZenViewSplitter = new (class { this.setTabsDocShellState(splitData.tabs, true); this.updateSplitViewButton(false); - this.applySplitters(splitData); - this.updateGridSizes(); + this.updateGridSizes(splitData); + this.applySplitters(splitData.widths.length , splitData.heights.length); + this.applyGridSizes(); } /** @@ -334,21 +335,49 @@ var gZenViewSplitter = new (class { } /** - * Adds splitters to tabBrowserPanel, adds default widths and heights to splitData + * Adds splitters to tabBrowserPanel + * + * @param nrOfColumns number of columns in the grid + * @param nrOfRows number of rows in the grid + */ + applySplitters(nrOfColumns, nrOfRows) { + this.removeSplitters(); + const vSplittersNeeded = (nrOfColumns - 1) * nrOfRows; + const hSplittersNeeded = nrOfRows - 1; + + const insertSplitter = (i, orient, gridIdx) => { + const splitter = document.createElement('div'); + splitter.className = 'zen-split-view-splitter'; + splitter.setAttribute('orient', orient); + splitter.setAttribute('gridIdx', gridIdx); + splitter.style.gridArea = `${orient === 'vertical' ? 'v' : 'h'}Splitter${i}`; + splitter.addEventListener('mousedown', this.handleSplitterMouseDown); + this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); + } + for (let i = 1; i <= vSplittersNeeded; i++) { + insertSplitter(i, 'vertical', Math.floor((i - 1) /nrOfRows) + 1); + } + for (let i = 1; i <= hSplittersNeeded; i++) { + insertSplitter(i, 'horizontal', i); + } + } + + /** + * Initialize splitData with default widths and heights if dimensions of grid don't match * * @param {object} splitData - The split data. */ - applySplitters(splitData) { + updateGridSizes(splitData) { const tabs = splitData.tabs; const gridType = splitData.gridType; let nrOfWidths = 1; let nrOfHeights = 1; - if (splitData.gridType === 'vsep') { + if (gridType === 'vsep') { nrOfWidths = tabs.length; - } else if (splitData.gridType === 'hsep') { + } else if (gridType === 'hsep') { nrOfHeights = tabs.length; - } else if (splitData.gridType === 'grid') { + } else if (gridType === 'grid') { nrOfWidths = tabs.length > 2 ? Math.ceil(tabs.length / 2) : 2; nrOfHeights = tabs.length > 2 ? 2 : 1; } @@ -356,30 +385,6 @@ var gZenViewSplitter = new (class { splitData.widths = Array(nrOfWidths).fill(100 / nrOfWidths); splitData.heights = Array(nrOfHeights).fill(100 / nrOfHeights); } - - let vSplittersNeeded = nrOfWidths - 1; - let hSplittersNeeded = nrOfHeights - 1; - if (splitData.gridType === 'grid' && tabs.length > 2) { - vSplittersNeeded = (tabs.length - ((tabs.length % 2 === 0) ? 2 : 1)); - hSplittersNeeded = 1; - } - this.removeSplitters(); - - const insertSplitter = (i, orient) => { - const splitter = document.createXULElement('div'); - splitter.className = 'zen-split-view-splitter'; - splitter.setAttribute('orient', orient); - splitter.setAttribute('gridIdx', gridType === 'grid' && orient === 'vertical' ? Math.ceil(i/2) : i); - splitter.style = `grid-area: ${orient === 'vertical' ? 'v' : 'h'}Splitter${i}`; - splitter.addEventListener('mousedown', this.handleSplitterMouseDown); - this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter); - } - for (let i = 1; i <= vSplittersNeeded; i++) { - insertSplitter(i, 'vertical'); - } - for (let i = 1; i <= hSplittersNeeded; i++) { - insertSplitter(i, 'horizontal'); - } } removeSplitters() { @@ -504,7 +509,7 @@ var gZenViewSplitter = new (class { } splitData[dimension][gridIdx - 1] += percentageChange; splitData[dimension][gridIdx] -= percentageChange; - this.updateGridSizes(); + this.applyGridSizes(); if (!max) prevPosition = dEvent[clientAxis]; }); } @@ -518,7 +523,10 @@ var gZenViewSplitter = new (class { setCursor(isVertical ? 'ew-resize' : 'n-resize'); } - updateGridSizes() { + /** + * Applies the grid column and row sizes + */ + applyGridSizes() { const splitData = this._data[this.currentView]; const columnGap = 'var(--zen-split-column-gap)'; const rowGap = 'var(--zen-split-row-gap)';