diff --git a/src/browser/base/content/browser-verticaltabs.js b/src/browser/base/content/browser-verticaltabs.js index a9ec5f22..501926ed 100644 --- a/src/browser/base/content/browser-verticaltabs.js +++ b/src/browser/base/content/browser-verticaltabs.js @@ -5,6 +5,47 @@ const VERTICAL_TABS_POSITION = 'pulse.tabs.vertical' const VERTICAL_TABS_COLLAPSE = 'pulse.tabs.vertical.collapse' +const VERTICAL_TABS_WIDTH = 'pulse.tabs.vertical.width' + +/** + * @param {HTMLElement} toInsertAfter This is the element that I want to insert content after + * @param {HTMLElement} toInsert The element to insert + * + * @throws {Error} If the element you want me to base insertions on has no parent + */ +function insertAfter(toInsertAfter, toInsert) { + const parent = toInsertAfter.parentNode + + if (!parent) { + throw new Error( + 'The element you want me to base insertions on has no parent' + ) + } + + if (toInsertAfter.nextSibling) { + parent.insertBefore(toInsert, toInsertAfter.nextSibling) + } else { + parent.appendChild(toInsert) + } +} + +/** + * Replace a tag with another tag with a different name + * @param {string} tagName The new tag name + * @param {HTMLElement?} initialTag The tag to be changed + */ +function changeXULTagName(tagName, initialTag) { + if (!initialTag) return + if (initialTag.tagName == tagName) return + + const newParent = document.createXULElement(tagName) + + for (const attr of initialTag.attributes) + newParent.setAttribute(attr.name, attr.value) + while (initialTag.firstChild) newParent.appendChild(initialTag.firstChild) + + initialTag.replaceWith(newParent) +} var VerticalTabs = { /** @@ -35,6 +76,13 @@ var VerticalTabs = { return document.getElementById('browser') }, + /** + * @return {HTMLElement?} + */ + get splitter() { + return document.getElementById('verticaltabs-splitter') + }, + /** * @return {Boolean} */ @@ -48,6 +96,8 @@ var VerticalTabs = { tabBrowserTabs: null, _initialized: false, + /** @type {MutationObserver?} */ + _widthObserver: null, init() { if (this._initialized) { @@ -79,9 +129,40 @@ var VerticalTabs = { .querySelector('#TabsToolbar .toolbar-items') ?.setAttribute('align', 'start') - document - .getElementById('TabsToolbar') - ?.setAttribute('collapse', this.browserCollapseTabs ? 'true' : 'false') + this.tabsToolbar?.setAttribute( + 'collapse', + this.browserCollapseTabs ? 'true' : 'false' + ) + this.tabsToolbar?.removeAttribute('flex') + changeXULTagName('vbox', this.tabsToolbar) + + this._widthObserver = new MutationObserver(this._mutationObserverCallback) + if (this.tabsToolbar) + this._widthObserver.observe(this.tabsToolbar, { attributes: true }) + + this.tabsToolbar?.setAttribute( + 'width', + Services.prefs.getIntPref(VERTICAL_TABS_WIDTH, 200) + ) + if (this.tabsToolbar) + this.tabsToolbar.style.width = `${Services.prefs.getIntPref( + VERTICAL_TABS_WIDTH, + 200 + )}px` + + if (!this.splitter) { + const separator = document.createXULElement('splitter') + separator.setAttribute('id', 'verticaltabs-splitter') + separator.setAttribute( + 'class', + 'chromeclass-extrachrome verticaltabs-splitter' + ) + separator.setAttribute('resizebefore', 'none') + separator.setAttribute('resizeafter', 'sibling') + + const tabs = this.tabsToolbar + if (tabs) tabs.parentElement?.insertBefore(separator, tabs) + } }, disableVerticalTabs() { @@ -96,6 +177,39 @@ var VerticalTabs = { document .querySelector('#TabsToolbar .toolbar-items') ?.setAttribute('align', 'end') + + if (this.tabsToolbar) { + changeXULTagName('toolbar', this.tabsToolbar) + this.tabsToolbar.setAttribute('flex', '1') + // Reset the resize value, or else the tabs will end up squished + this.tabsToolbar.style.width = '' + } + + if (this.splitter) { + this.splitter.remove() + } + + if (this._widthObserver) { + this._widthObserver.disconnect() + this._widthObserver = null + } + }, + + /** + * @param {MutationRecord[]} mutationsList + * @param {MutationObserver} _observer + */ + _mutationObserverCallback(mutationsList, _observer) { + for (const mutation of mutationsList) { + if (mutation.type === 'attributes' && mutation.attributeName == 'width') { + const tabsToolbar = document.getElementById('TabsToolbar') + + Services.prefs.setIntPref( + VERTICAL_TABS_WIDTH, + parseInt(tabsToolbar?.getAttribute('width') || '100') + ) + } + } }, /** diff --git a/src/browser/themes/pulse/vertical_tabs.css b/src/browser/themes/pulse/vertical_tabs.css index 46ebf7cc..be08bb7c 100644 --- a/src/browser/themes/pulse/vertical_tabs.css +++ b/src/browser/themes/pulse/vertical_tabs.css @@ -3,6 +3,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +:root { + --vertical-tabs-max-width: 350px; +} + #browser #TabsToolbar .titlebar-spacer, #browser #TabsToolbar .titlebar-buttonbox-container, #browser #TabsToolbar #alltabs-button { @@ -10,14 +14,29 @@ } #navigator-toolbox-background[verticaltabs='true'] #titlebar { - display: none; + display: none; } #browser #TabsToolbar { background-color: -moz-Dialog; - max-width: 350px; + max-width: var(--vertical-tabs-max-width); + -moz-box-ordinal-group: 0; +} + +#browser #TabsToolbar:not([collapse='true']) { + min-width: 76px; +} - transition: all 200ms ease-in-out; +#browser #TabsToolbar[collapse='true'] { + max-width: calc( + 16px + 2 * var(--inline-tab-padding) + 2 * var(--tab-block-margin) + ); +} + +/** On collapse hover of tabs or splitter, the max width should increase again */ +#browser #TabsToolbar[collapse='true']:hover, +#verticaltabs-splitter:hover ~ #TabsToolbar[collapse='true'] { + max-width: var(--vertical-tabs-max-width); } #browser #TabsToolbar:-moz-lwtheme { @@ -36,9 +55,6 @@ /* Stops the tabs from getting squished or stretched */ min-height: unset; max-height: var(--tab-min-height); - - /* Animate tabs out vertically */ - transition: min-height 100ms ease-out, max-height 100ms ease-out; } /* Put the new tab button on the bottom */ @@ -93,12 +109,15 @@ margin-inline-start: none; } -#browser #TabsToolbar[collapse='true']:not(:hover) { - max-width: calc( - 16px + 2 * var(--inline-tab-padding) + 2 * var(--tab-block-margin) - ); +#browser #TabsToolbar[collapse='true'] .tabbrowser-tab { + /* Animate tabs out vertically */ + transition: min-height 100ms ease-out, max-height 100ms ease-out; +} + +#verticaltabs-splitter { + -moz-box-ordinal-group: 1; } :root[sizemode='fullscreen'] #browser #TabsToolbar { - display: none; -} \ No newline at end of file + display: none; +}