mirror of
https://github.com/zen-browser/desktop
synced 2026-04-25 17:15:00 +02:00
no-bug: Add alt+click for splitting tabs and other polishing details (gh-13308)
This commit is contained in:
@@ -58,6 +58,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL!
|
||||
zen-tabs-renamed = Tab has been successfully renamed!
|
||||
zen-background-tab-opened-toast = New background tab opened!
|
||||
zen-workspace-renamed-toast = Workspace has been successfully renamed!
|
||||
zen-split-view-limit-toast = Can't add more panels to the split view!
|
||||
|
||||
zen-toggle-compact-mode-button =
|
||||
.label = Compact Mode
|
||||
|
||||
@@ -6,7 +6,7 @@ tab-zen-split-tabs =
|
||||
.label =
|
||||
{ $tabCount ->
|
||||
[-1] Split out tab
|
||||
[1] Join Tab (multiple selected tabs needed)
|
||||
[1] Add split view...
|
||||
*[other] Join { $tabCount } Tabs
|
||||
}
|
||||
.accesskey = S
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
value: 300 # in milliseconds
|
||||
|
||||
- name: zen.glance.deactivate-docshell-during-animation
|
||||
value: true
|
||||
value: false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
|
||||
index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78a831e8ee 100644
|
||||
index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..3ac8d2facb224ae39d2199f2c5ec08b77e0c1dfd 100644
|
||||
--- a/browser/components/tabbrowser/content/tab.js
|
||||
+++ b/browser/components/tabbrowser/content/tab.js
|
||||
@@ -21,6 +21,7 @@
|
||||
@@ -151,7 +151,18 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78
|
||||
on_click(event) {
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
@@ -620,11 +659,12 @@
|
||||
@@ -617,14 +656,23 @@
|
||||
trigger: "alt_click",
|
||||
});
|
||||
}
|
||||
+ if (
|
||||
+ !event.target.classList.contains("tab-close-button") &&
|
||||
+ !event.target.classList.contains("tab-icon-overlay") &&
|
||||
+ !event.target.classList.contains("tab-audio-button") &&
|
||||
+ !this.splitView
|
||||
+ ) {
|
||||
+ gZenViewSplitter.contextSplitTabs(this);
|
||||
+ }
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -165,7 +176,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78
|
||||
gBrowser.multiSelectedTabsCount > 0 &&
|
||||
!event.target.classList.contains("tab-close-button") &&
|
||||
!event.target.classList.contains("tab-icon-overlay") &&
|
||||
@@ -636,8 +676,9 @@
|
||||
@@ -636,8 +684,9 @@
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -177,7 +188,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78
|
||||
) {
|
||||
if (this.activeMediaBlocked) {
|
||||
if (this.multiselected) {
|
||||
@@ -655,7 +696,7 @@
|
||||
@@ -655,7 +704,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,7 +197,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78
|
||||
if (this.multiselected) {
|
||||
gBrowser.removeMultiSelectedTabs(
|
||||
lazy.TabMetrics.userTriggeredContext(
|
||||
@@ -675,6 +716,14 @@
|
||||
@@ -675,6 +724,14 @@
|
||||
// (see tabbrowser-tabs 'click' handler).
|
||||
gBrowser.tabContainer._blockDblClick = true;
|
||||
}
|
||||
@@ -201,7 +212,7 @@ index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..439766a40df5632ad790ab54a0c6af78
|
||||
}
|
||||
|
||||
on_dblclick(event) {
|
||||
@@ -698,6 +747,8 @@
|
||||
@@ -698,6 +755,8 @@
|
||||
animate: true,
|
||||
triggeringEvent: event,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25b964ccbb 100644
|
||||
index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35e66ff55a 100644
|
||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -413,6 +413,7 @@
|
||||
@@ -587,7 +587,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -5603,6 +5757,13 @@
|
||||
@@ -5603,6 +5757,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -597,11 +597,12 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
+ this.selectedTab = newTab;
|
||||
+ }
|
||||
+ }
|
||||
+ animate &&= !aTab.splitView;
|
||||
+
|
||||
let isVisibleTab = aTab.visible;
|
||||
// We have to sample the tab width now, since _beginRemoveTab might
|
||||
// end up modifying the DOM in such a way that aTab gets a new
|
||||
@@ -5610,6 +5771,9 @@
|
||||
@@ -5610,6 +5772,9 @@
|
||||
// state).
|
||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||
let isLastTab = this.#isLastTabInWindow(aTab);
|
||||
@@ -611,7 +612,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
if (
|
||||
!this._beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -5621,13 +5785,14 @@
|
||||
@@ -5621,13 +5786,14 @@
|
||||
telemetrySource,
|
||||
})
|
||||
) {
|
||||
@@ -627,7 +628,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
let lockTabSizing =
|
||||
!this.tabContainer.verticalMode &&
|
||||
!aTab.pinned &&
|
||||
@@ -5658,7 +5823,13 @@
|
||||
@@ -5658,7 +5824,13 @@
|
||||
// We're not animating, so we can cancel the animation stopwatch.
|
||||
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
||||
aTab._closeTimeAnimTimerId = null;
|
||||
@@ -642,7 +643,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5792,7 +5963,7 @@
|
||||
@@ -5792,7 +5964,7 @@
|
||||
closeWindowWithLastTab != null
|
||||
? closeWindowWithLastTab
|
||||
: !window.toolbar.visible ||
|
||||
@@ -651,7 +652,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
|
||||
if (closeWindow) {
|
||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||
@@ -5816,6 +5987,7 @@
|
||||
@@ -5816,6 +5988,7 @@
|
||||
|
||||
newTab = true;
|
||||
}
|
||||
@@ -659,7 +660,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||
|
||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||
@@ -5856,13 +6028,7 @@
|
||||
@@ -5856,13 +6029,7 @@
|
||||
aTab._mouseleave();
|
||||
|
||||
if (newTab) {
|
||||
@@ -674,7 +675,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
} else {
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -5995,6 +6161,7 @@
|
||||
@@ -5995,6 +6162,7 @@
|
||||
this.tabs[i]._tPos = i;
|
||||
}
|
||||
|
||||
@@ -682,7 +683,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
if (!this._windowIsClosing) {
|
||||
// update tab close buttons state
|
||||
this.tabContainer._updateCloseButtons();
|
||||
@@ -6225,6 +6392,7 @@
|
||||
@@ -6225,6 +6393,7 @@
|
||||
}
|
||||
|
||||
let excludeTabs = new Set(aExcludeTabs);
|
||||
@@ -690,7 +691,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
|
||||
// If this tab has a successor, it should be selectable, since
|
||||
// hiding or closing a tab removes that tab as a successor.
|
||||
@@ -6237,15 +6405,22 @@
|
||||
@@ -6237,15 +6406,22 @@
|
||||
!excludeTabs.has(aTab.owner) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||
) {
|
||||
@@ -715,7 +716,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
direction: 1,
|
||||
filter: _tab => remainingTabs.includes(_tab),
|
||||
@@ -6259,7 +6434,7 @@
|
||||
@@ -6259,7 +6435,7 @@
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
@@ -724,7 +725,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
}
|
||||
|
||||
// If no qualifying visible tab was found, see if there is a tab in
|
||||
@@ -6280,7 +6455,7 @@
|
||||
@@ -6280,7 +6456,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
@@ -733,7 +734,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
}
|
||||
|
||||
_blurTab(aTab) {
|
||||
@@ -6291,7 +6466,7 @@
|
||||
@@ -6291,7 +6467,7 @@
|
||||
* @returns {boolean}
|
||||
* False if swapping isn't permitted, true otherwise.
|
||||
*/
|
||||
@@ -742,7 +743,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
// Do not allow transfering a private tab to a non-private window
|
||||
// and vice versa.
|
||||
if (
|
||||
@@ -6345,6 +6520,7 @@
|
||||
@@ -6345,6 +6521,7 @@
|
||||
// fire the beforeunload event in the process. Close the other
|
||||
// window if this was its last tab.
|
||||
if (
|
||||
@@ -750,7 +751,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
!remoteBrowser._beginRemoveTab(aOtherTab, {
|
||||
adoptedByTab: aOurTab,
|
||||
closeWindowWithLastTab: true,
|
||||
@@ -6356,7 +6532,7 @@
|
||||
@@ -6356,7 +6533,7 @@
|
||||
// If this is the last tab of the window, hide the window
|
||||
// immediately without animation before the docshell swap, to avoid
|
||||
// about:blank being painted.
|
||||
@@ -759,7 +760,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
if (closeWindow) {
|
||||
let win = aOtherTab.ownerGlobal;
|
||||
win.windowUtils.suppressAnimation(true);
|
||||
@@ -6484,11 +6660,13 @@
|
||||
@@ -6484,11 +6661,13 @@
|
||||
}
|
||||
|
||||
// Finish tearing down the tab that's going away.
|
||||
@@ -773,7 +774,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
|
||||
this.setTabTitle(aOurTab);
|
||||
|
||||
@@ -6690,10 +6868,10 @@
|
||||
@@ -6690,10 +6869,10 @@
|
||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||
}
|
||||
|
||||
@@ -786,7 +787,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||
@@ -6753,7 +6931,8 @@
|
||||
@@ -6753,7 +6932,8 @@
|
||||
* @param {object} [aOptions={}]
|
||||
* Key-value pairs that will be serialized into the features string.
|
||||
*/
|
||||
@@ -796,7 +797,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
if (this.tabs.length == 1) {
|
||||
return null;
|
||||
}
|
||||
@@ -6770,7 +6949,7 @@
|
||||
@@ -6770,7 +6950,7 @@
|
||||
// tell a new window to take the "dropped" tab
|
||||
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
args.appendElement(aTab.splitview ?? aTab);
|
||||
@@ -805,7 +806,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
private: PrivateBrowsingUtils.isWindowPrivate(window),
|
||||
features: Object.entries(aOptions)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
@@ -6778,6 +6957,8 @@
|
||||
@@ -6778,6 +6958,8 @@
|
||||
openerWindow: window,
|
||||
args,
|
||||
});
|
||||
@@ -814,7 +815,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6890,7 +7071,7 @@
|
||||
@@ -6890,7 +7072,7 @@
|
||||
* `true` if element is a `<tab-group>`
|
||||
*/
|
||||
isTabGroup(element) {
|
||||
@@ -823,7 +824,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6975,8 +7156,8 @@
|
||||
@@ -6975,8 +7157,8 @@
|
||||
}
|
||||
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
@@ -834,7 +835,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
} else {
|
||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||
}
|
||||
@@ -7005,13 +7186,19 @@
|
||||
@@ -7005,13 +7187,19 @@
|
||||
this.#handleTabMove(
|
||||
element,
|
||||
() => {
|
||||
@@ -856,7 +857,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
let useAfter = false;
|
||||
if (this.isTab(element)) {
|
||||
useAfter = neighbor && tabIndex > element._tPos;
|
||||
@@ -7076,23 +7263,31 @@
|
||||
@@ -7076,23 +7264,31 @@
|
||||
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
||||
if (this.isTabGroupLabel(targetElement)) {
|
||||
targetElement = targetElement.group;
|
||||
@@ -894,7 +895,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||
// If the caller asks to move an unpinned element next to a pinned
|
||||
// tab, move the unpinned element to be the first unpinned element
|
||||
@@ -7105,12 +7300,35 @@
|
||||
@@ -7105,12 +7301,35 @@
|
||||
// move the tab group right before the first unpinned tab.
|
||||
// 4. Moving a tab group and the first unpinned tab is grouped:
|
||||
// move the tab group right before the first unpinned tab's tab group.
|
||||
@@ -931,7 +932,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
|
||||
// We want to include the splitview wrapper if it's the targetElement, but
|
||||
// not in the case where we want to reverse tabs within the same splitview.
|
||||
@@ -7119,6 +7337,7 @@
|
||||
@@ -7119,6 +7338,7 @@
|
||||
}
|
||||
|
||||
let getContainer = () =>
|
||||
@@ -939,7 +940,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
element.pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -7127,11 +7346,15 @@
|
||||
@@ -7127,11 +7347,15 @@
|
||||
element,
|
||||
() => {
|
||||
if (moveBefore) {
|
||||
@@ -956,7 +957,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
}
|
||||
},
|
||||
metricsContext
|
||||
@@ -7205,10 +7428,10 @@
|
||||
@@ -7205,10 +7429,10 @@
|
||||
* @param {TabMetricsContext} [metricsContext]
|
||||
*/
|
||||
moveTabToExistingGroup(aTab, aGroup, metricsContext) {
|
||||
@@ -969,7 +970,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
return;
|
||||
}
|
||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||
@@ -7281,6 +7504,7 @@
|
||||
@@ -7281,6 +7505,7 @@
|
||||
|
||||
let state = {
|
||||
tabIndex: tab._tPos,
|
||||
@@ -977,7 +978,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
};
|
||||
if (tab.visible) {
|
||||
state.elementIndex = tab.elementIndex;
|
||||
@@ -7312,7 +7536,7 @@
|
||||
@@ -7312,7 +7537,7 @@
|
||||
let changedSplitView =
|
||||
previousTabState.splitViewId != currentTabState.splitViewId;
|
||||
|
||||
@@ -986,7 +987,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
tab.dispatchEvent(
|
||||
new CustomEvent("TabMove", {
|
||||
bubbles: true,
|
||||
@@ -7354,6 +7578,10 @@
|
||||
@@ -7354,6 +7579,10 @@
|
||||
|
||||
moveActionCallback();
|
||||
|
||||
@@ -997,7 +998,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||
// changed.
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
@@ -7404,7 +7632,22 @@
|
||||
@@ -7404,7 +7633,22 @@
|
||||
* @returns {object}
|
||||
* The new tab in the current window, null if the tab couldn't be adopted.
|
||||
*/
|
||||
@@ -1021,7 +1022,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
// Swap the dropped tab with a new one we create and then close
|
||||
// it in the other window (making it seem to have moved between
|
||||
// windows). We also ensure that the tab we create to swap into has
|
||||
@@ -7447,6 +7690,8 @@
|
||||
@@ -7447,6 +7691,8 @@
|
||||
}
|
||||
params.skipLoad = true;
|
||||
let newTab = this.addWebTab("about:blank", params);
|
||||
@@ -1030,7 +1031,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
|
||||
aTab.container.tabDragAndDrop.finishAnimateTabMove();
|
||||
|
||||
@@ -8149,7 +8394,7 @@
|
||||
@@ -8149,7 +8395,7 @@
|
||||
// preventDefault(). It will still raise the window if appropriate.
|
||||
return;
|
||||
}
|
||||
@@ -1039,7 +1040,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
window.focus();
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
@@ -8166,7 +8411,6 @@
|
||||
@@ -8166,7 +8412,6 @@
|
||||
|
||||
on_TabGroupCollapse(aEvent) {
|
||||
aEvent.target.tabs.forEach(tab => {
|
||||
@@ -1047,7 +1048,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8500,7 +8744,9 @@
|
||||
@@ -8500,7 +8745,9 @@
|
||||
|
||||
let filter = this._tabFilters.get(tab);
|
||||
if (filter) {
|
||||
@@ -1057,7 +1058,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
|
||||
let listener = this._tabListeners.get(tab);
|
||||
if (listener) {
|
||||
@@ -9306,6 +9552,7 @@
|
||||
@@ -9306,6 +9553,7 @@
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this.mTab.setAttribute("busy", "true");
|
||||
@@ -1065,7 +1066,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||
}
|
||||
@@ -9386,6 +9633,7 @@
|
||||
@@ -9386,6 +9634,7 @@
|
||||
// known defaults. Note we use the original URL since about:newtab
|
||||
// redirects to a prerendered page.
|
||||
const shouldRemoveFavicon =
|
||||
@@ -1073,7 +1074,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
!this.mBrowser.mIconURL &&
|
||||
!ignoreBlank &&
|
||||
!(originalLocation.spec in FAVICON_DEFAULTS);
|
||||
@@ -9560,13 +9808,6 @@
|
||||
@@ -9560,13 +9809,6 @@
|
||||
this.mBrowser.originalURI = aRequest.originalURI;
|
||||
}
|
||||
|
||||
@@ -1087,7 +1088,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..642b74cdf0edc63e5cf4ca8c69481e25
|
||||
}
|
||||
|
||||
let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
|
||||
@@ -10450,7 +10691,7 @@ var TabContextMenu = {
|
||||
@@ -10450,7 +10692,7 @@ var TabContextMenu = {
|
||||
);
|
||||
contextUnpinSelectedTabs.hidden =
|
||||
!this.contextTab.pinned || !this.multiselected;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css
|
||||
index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d3409383217e2ca 100644
|
||||
index 203b546933842f4b0134188fda020c4db4dcd0d2..0d67deabac2984574636248a16e58d1669f2e1a4 100644
|
||||
--- a/browser/themes/shared/tabbrowser/tabs.css
|
||||
+++ b/browser/themes/shared/tabbrowser/tabs.css
|
||||
@@ -24,7 +24,7 @@
|
||||
@@ -97,7 +97,15 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
&:is([soundplaying], [muted], [activemedia-blocked]) {
|
||||
display: flex;
|
||||
}
|
||||
@@ -1614,7 +1606,7 @@ tab-group {
|
||||
@@ -1048,7 +1040,6 @@ tab-split-view-wrapper[dragtarget] {
|
||||
.tabbrowser-tab:is([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected]),
|
||||
.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected]),
|
||||
#tabbrowser-tabs[orient="vertical"] .tabbrowser-tab > .tab-stack > .tab-content[titlechanged]:not([selected]) {
|
||||
- background-image: radial-gradient(circle, var(--tab-attention-dot-color), var(--tab-attention-dot-color) 2px, transparent 2px);
|
||||
background-position: center bottom 6.5px;
|
||||
background-size: 4px 4px;
|
||||
background-repeat: no-repeat;
|
||||
@@ -1614,7 +1605,7 @@ tab-group {
|
||||
}
|
||||
|
||||
#tabbrowser-tabs[orient="vertical"][expanded] {
|
||||
@@ -106,7 +114,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
&[movingtab][movingtab-addToGroup]:not([movingtab-group], [movingtab-ungroup]) tab-group > .tabbrowser-tab:is(:active, [multiselected]) {
|
||||
margin-inline-start: var(--space-medium);
|
||||
}
|
||||
@@ -2089,7 +2081,7 @@ tab-group {
|
||||
@@ -2089,7 +2080,7 @@ tab-group {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +123,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
#vertical-tabs-newtab-button {
|
||||
appearance: none;
|
||||
min-height: var(--tab-min-height);
|
||||
@@ -2100,7 +2092,7 @@ tab-group {
|
||||
@@ -2100,7 +2091,7 @@ tab-group {
|
||||
margin-inline: var(--tab-inner-inline-margin);
|
||||
|
||||
#tabbrowser-tabs[orient="vertical"]:not([expanded]) & > .toolbarbutton-text {
|
||||
@@ -124,7 +132,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -2124,7 +2116,7 @@ tab-group {
|
||||
@@ -2124,7 +2115,7 @@ tab-group {
|
||||
* flex container. #tabs-newtab-button is a child of the arrowscrollbox where
|
||||
* we don't want a gap (between tabs), so we have to add some margin.
|
||||
*/
|
||||
@@ -133,7 +141,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
margin-block: var(--tab-block-margin);
|
||||
}
|
||||
|
||||
@@ -2312,7 +2304,6 @@ tab-group {
|
||||
@@ -2312,7 +2303,6 @@ tab-group {
|
||||
|
||||
&:not([expanded]) {
|
||||
.tabbrowser-tab[pinned] {
|
||||
@@ -141,7 +149,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
}
|
||||
|
||||
.tab-background {
|
||||
@@ -2352,8 +2343,8 @@ tab-group {
|
||||
@@ -2352,8 +2342,8 @@ tab-group {
|
||||
display: block;
|
||||
position: absolute;
|
||||
inset: auto;
|
||||
@@ -152,7 +160,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
|
||||
&:-moz-window-inactive {
|
||||
background-image:
|
||||
@@ -2438,9 +2429,6 @@ tab-group {
|
||||
@@ -2438,9 +2428,6 @@ tab-group {
|
||||
|
||||
:root:not([privatebrowsingmode]) :is(toolbarbutton, toolbarpaletteitem) ~ #tabbrowser-tabs,
|
||||
:root[privatebrowsingmode] :is(toolbarbutton:not(#firefox-view-button), toolbarpaletteitem:not(#wrapper-firefox-view-button)) ~ #tabbrowser-tabs {
|
||||
@@ -162,7 +170,7 @@ index 203b546933842f4b0134188fda020c4db4dcd0d2..011bcfb023d0cabd55dfa7e49d340938
|
||||
}
|
||||
|
||||
:root[privatebrowsingmode] :is(#firefox-view-button, #menu_openFirefoxView) {
|
||||
@@ -2472,7 +2460,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button {
|
||||
@@ -2472,7 +2459,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button {
|
||||
list-style-image: url(chrome://global/skin/icons/plus.svg);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,10 +101,12 @@ window.gZenUIManager = {
|
||||
) {
|
||||
const yValues = rawKeyframes.y || [];
|
||||
const xValues = rawKeyframes.x || [];
|
||||
const scaleValues = rawKeyframes.scale || [];
|
||||
const scaleYValues = rawKeyframes.scaleY || [];
|
||||
const scaleXValues = rawKeyframes.scaleX || [];
|
||||
delete rawKeyframes.y;
|
||||
delete rawKeyframes.x;
|
||||
delete rawKeyframes.scale;
|
||||
delete rawKeyframes.scaleY;
|
||||
delete rawKeyframes.scaleX;
|
||||
rawKeyframes.transform = [];
|
||||
if (
|
||||
yValues.length !== 0 &&
|
||||
@@ -116,14 +118,17 @@ window.gZenUIManager = {
|
||||
const keyframeLength = Math.max(
|
||||
yValues.length,
|
||||
xValues.length,
|
||||
scaleValues.length
|
||||
scaleYValues.length,
|
||||
scaleXValues.length
|
||||
);
|
||||
for (let i = 0; i < keyframeLength; i++) {
|
||||
const y = yValues[i] !== undefined ? `translateY(${yValues[i]}px)` : "";
|
||||
const x = xValues[i] !== undefined ? `translateX(${xValues[i]}px)` : "";
|
||||
const scale =
|
||||
scaleValues[i] !== undefined ? `scale(${scaleValues[i]})` : "";
|
||||
rawKeyframes.transform.push(`${x} ${y} ${scale}`.trim());
|
||||
const scaleY =
|
||||
scaleYValues[i] !== undefined ? `scaleY(${scaleYValues[i]})` : "";
|
||||
const scaleX =
|
||||
scaleXValues[i] !== undefined ? `scaleX(${scaleXValues[i]})` : "";
|
||||
rawKeyframes.transform.push(`${x} ${y} ${scaleX} ${scaleY}`.trim());
|
||||
}
|
||||
}
|
||||
let keyframes = [];
|
||||
|
||||
@@ -58,18 +58,15 @@
|
||||
}
|
||||
|
||||
@keyframes zen-progress-bar-pulse {
|
||||
0% {
|
||||
transform: scale(0.8) translate(-50%, -50%);
|
||||
from {
|
||||
transform: scale(0.85) translate(-50%, -50%);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
|
||||
to {
|
||||
transform: scale(0.95) translate(-50%, -50%);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.8) translate(-50%, -50%);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-progress-bar-long-load {
|
||||
|
||||
@@ -695,6 +695,7 @@
|
||||
:root:is([zen-no-padding="true"], [inDOMFullscreen="true"]) & {
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
background: var(--zen-loading-progress-bar-color);
|
||||
@@ -707,8 +708,10 @@
|
||||
transform .3s ease-in-out;
|
||||
overflow: clip;
|
||||
pointer-events: none;
|
||||
animation: zen-progress-bar-pulse 1.5s linear infinite forwards;
|
||||
animation: zen-progress-bar-pulse 1s ease-in-out infinite forwards;
|
||||
animation-direction: alternate;
|
||||
transform-origin: 0 0;
|
||||
contain: content;
|
||||
|
||||
&[long-load="true"] {
|
||||
opacity: 1;
|
||||
|
||||
@@ -50,7 +50,7 @@ export class ZenProgressBar extends ZenUIComponent {
|
||||
this.window.gZenUIManager.elementAnimate(
|
||||
this.#element,
|
||||
{
|
||||
opacity: [0, 0.6],
|
||||
opacity: [0, 0.8],
|
||||
},
|
||||
{
|
||||
duration: 400,
|
||||
|
||||
@@ -119,6 +119,10 @@
|
||||
init() {
|
||||
super.init();
|
||||
this.handle_windowDragEnter = this.handle_windowDragEnter.bind(this);
|
||||
gZenWorkspaces.workspaceIcons.addEventListener(
|
||||
"dragover",
|
||||
this.handle_spaceIconDragOver.bind(this)
|
||||
);
|
||||
window.addEventListener(
|
||||
"dragleave",
|
||||
this.handle_windowDragLeave.bind(this),
|
||||
@@ -676,6 +680,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
#onSpaceChanged(spaceChanged, dt) {
|
||||
if (AppConstants.platform !== "macosx") {
|
||||
// See the hack in #createDragImageForTabs for more details which
|
||||
// explains why we need to do this on non-macOS platforms.
|
||||
return;
|
||||
}
|
||||
let tabs = this.originalDragImageArgs[0].children;
|
||||
const { isDarkMode, isExplicitMode } =
|
||||
gZenThemePicker.getGradientForWorkspace(spaceChanged, {
|
||||
getGradient: false,
|
||||
});
|
||||
for (let tab of tabs) {
|
||||
if (isExplicitMode) {
|
||||
tab.style.colorScheme = isDarkMode ? "dark" : "light";
|
||||
} else {
|
||||
tab.style.colorScheme = "";
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
dt.updateDragImage(...this.originalDragImageArgs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#handle_sidebarDragOver(event) {
|
||||
const dt = event.dataTransfer;
|
||||
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
@@ -696,28 +725,7 @@
|
||||
/* Disable wrapping */ true
|
||||
)
|
||||
.then(spaceChanged => {
|
||||
if (AppConstants.platform !== "macosx") {
|
||||
// See the hack in #createDragImageForTabs for more details which
|
||||
// explains why we need to do this on non-macOS platforms.
|
||||
return;
|
||||
}
|
||||
let tabs = this.originalDragImageArgs[0].children;
|
||||
const { isDarkMode, isExplicitMode } =
|
||||
gZenThemePicker.getGradientForWorkspace(spaceChanged, {
|
||||
getGradient: false,
|
||||
});
|
||||
for (let tab of tabs) {
|
||||
if (isExplicitMode) {
|
||||
tab.style.colorScheme = isDarkMode ? "dark" : "light";
|
||||
} else {
|
||||
tab.style.colorScheme = "";
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
dt.updateDragImage(...this.originalDragImageArgs);
|
||||
});
|
||||
});
|
||||
this.#onSpaceChanged(spaceChanged, dt);
|
||||
});
|
||||
this.#changeSpaceTimer = null;
|
||||
}, this._dndSwitchSpaceDelay);
|
||||
@@ -727,6 +735,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
handle_spaceIconDragOver(event) {
|
||||
const dt = event.dataTransfer;
|
||||
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
if (draggedTab.hasAttribute("zen-essential")) {
|
||||
return;
|
||||
}
|
||||
const target = event.target;
|
||||
const spaceId = target.getAttribute("zen-workspace-id");
|
||||
if (!spaceId) {
|
||||
return;
|
||||
}
|
||||
this.clearDragOverVisuals();
|
||||
const currentSpaceId = gZenWorkspaces.activeWorkspace;
|
||||
if (spaceId === currentSpaceId || gZenWorkspaces._animatingChange) {
|
||||
return;
|
||||
}
|
||||
gZenWorkspaces.changeWorkspaceWithID(spaceId).then(spaceChanged => {
|
||||
this.#onSpaceChanged(spaceChanged, dt);
|
||||
});
|
||||
}
|
||||
|
||||
#handle_tabDragOverToSplit(event) {
|
||||
if (!this._dndSplitEnabled) {
|
||||
return;
|
||||
@@ -1238,18 +1267,24 @@
|
||||
) {
|
||||
let lastTab = gBrowser.tabs.at(-1);
|
||||
let pinnedTabsCount = gBrowser._numVisiblePinTabsWithoutCollapsed;
|
||||
let isHoveringSeparator =
|
||||
event.target.parentElement.classList.contains(
|
||||
"zen-workspace-pinned-tabs-section"
|
||||
);
|
||||
|
||||
// Only if there are no normal tabs to drop after
|
||||
showIndicatorUnderNewTabButton =
|
||||
lastTab.hasAttribute("zen-empty-tab");
|
||||
let useLastPinnd =
|
||||
(hoveringPeriphery ||
|
||||
(showIndicatorUnderNewTabButton &&
|
||||
!(pinnedTabsCount - gBrowser._numZenEssentials))) &&
|
||||
Services.prefs.getBoolPref("zen.view.show-newtab-button-top");
|
||||
let useLastPinned =
|
||||
(showIndicatorUnderNewTabButton &&
|
||||
!(pinnedTabsCount - gBrowser._numZenEssentials) &&
|
||||
Services.prefs.getBoolPref("zen.view.show-newtab-button-top")) ||
|
||||
isHoveringSeparator;
|
||||
dropElement =
|
||||
(useLastPinnd
|
||||
? this._tabbrowserTabs.ariaFocusableItems.at(pinnedTabsCount)
|
||||
(useLastPinned
|
||||
? this._tabbrowserTabs.ariaFocusableItems.at(
|
||||
pinnedTabsCount ? pinnedTabsCount - 1 : 0
|
||||
)
|
||||
: this._tabbrowserTabs.ariaFocusableItems.at(-1)) || lastTab;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,8 +744,14 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
|
||||
const onKeyDown = event => {
|
||||
// Arrow down and up to navigate through the list
|
||||
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
||||
if (
|
||||
event.key === "ArrowDown" ||
|
||||
event.key === "ArrowUp" ||
|
||||
event.key === "Tab"
|
||||
) {
|
||||
event.preventDefault();
|
||||
let isUp =
|
||||
event.key === "ArrowUp" || (event.key === "Tab" && event.shiftKey);
|
||||
const items = Array.from(tabsList.children).filter(
|
||||
item => !item.hidden
|
||||
);
|
||||
@@ -755,9 +761,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
let index = items.indexOf(
|
||||
tabsList.querySelector(".folders-tabs-list-item[selected]")
|
||||
);
|
||||
if (event.key === "ArrowDown") {
|
||||
if (!isUp) {
|
||||
index = (index + 1) % items.length;
|
||||
} else if (event.key === "ArrowUp") {
|
||||
} else {
|
||||
index = (index - 1 + items.length) % items.length;
|
||||
}
|
||||
items.forEach(item => item.removeAttribute("selected"));
|
||||
|
||||
@@ -39,8 +39,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
ARC_HEIGHT_RATIO: 0.2, // Arc height = distance * ratio (capped at MAX_ARC_HEIGHT)
|
||||
});
|
||||
|
||||
#GLANCE_ANIMATION_DURATION =
|
||||
Services.prefs.getIntPref("zen.glance.animation-duration") / 1000;
|
||||
#GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref(
|
||||
"zen.glance.animation-duration"
|
||||
);
|
||||
|
||||
init() {
|
||||
this.#setupEventListeners();
|
||||
@@ -294,7 +295,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
{
|
||||
duration: 0.2,
|
||||
type: "spring",
|
||||
delay: this.#GLANCE_ANIMATION_DURATION - 0.2,
|
||||
delay: this.#GLANCE_ANIMATION_DURATION / 1000 - 0.2,
|
||||
bounce: 0,
|
||||
}
|
||||
);
|
||||
@@ -450,7 +451,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
opacity: [1, 0.3],
|
||||
},
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION,
|
||||
duration: this.#GLANCE_ANIMATION_DURATION / 1000,
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
}
|
||||
@@ -536,13 +537,13 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
// nice fade-in effect to the content. But if it doesn't exist,
|
||||
// we just fall back to always showing the browser directly.
|
||||
if (data.elementData) {
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
gZenUIManager
|
||||
.elementAnimate(
|
||||
this.contentWrapper,
|
||||
{ opacity: [0, 1] },
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION / 4,
|
||||
easing: "easeInOut",
|
||||
easing: "ease-in-out",
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
@@ -559,12 +560,12 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
browserElement.zenModeActive = false;
|
||||
browserElement.docShellIsActive = false;
|
||||
}
|
||||
gZenUIManager.motion
|
||||
.animate(this.browserWrapper, arcSequence, {
|
||||
gZenUIManager
|
||||
.elementAnimate(this.browserWrapper, arcSequence, {
|
||||
duration: gZenUIManager.testingEnabled
|
||||
? 0
|
||||
: this.#GLANCE_ANIMATION_DURATION,
|
||||
ease: "easeInOut",
|
||||
easing: "ease-in-out",
|
||||
})
|
||||
.then(() => {
|
||||
if (shouldDeactivateDocShell) {
|
||||
@@ -971,7 +972,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
{
|
||||
duration: 0.2,
|
||||
type: "spring",
|
||||
bounce: this.#GLANCE_ANIMATION_DURATION - 0.1,
|
||||
bounce: this.#GLANCE_ANIMATION_DURATION / 1000 - 0.1,
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
@@ -1019,7 +1020,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
opacity: [0.3, 1],
|
||||
},
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION / 1.5,
|
||||
duration: this.#GLANCE_ANIMATION_DURATION / 1000 / 1.5,
|
||||
type: "spring",
|
||||
bounce: 0,
|
||||
}
|
||||
@@ -1062,10 +1063,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
this.browserWrapper.style.width = "";
|
||||
this.browserWrapper.style.height = "";
|
||||
|
||||
gZenUIManager.motion
|
||||
.animate(this.browserWrapper, arcSequence, {
|
||||
gZenUIManager
|
||||
.elementAnimate(this.browserWrapper, arcSequence, {
|
||||
duration: this.#GLANCE_ANIMATION_DURATION,
|
||||
ease: "easeOut",
|
||||
easing: "ease-out",
|
||||
})
|
||||
.then(() => {
|
||||
// Remove element preview after closing animation
|
||||
@@ -1573,9 +1574,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
this.#handleZenFolderPinning();
|
||||
gBrowser.moveTabAfter(this.#currentTab, this.#currentParentTab);
|
||||
|
||||
const browserRect = window.windowUtils.getBoundsWithoutFlushing(
|
||||
this.browserWrapper
|
||||
);
|
||||
this.#prepareTabForFullOpen();
|
||||
|
||||
const sidebarButtons = this.browserWrapper.querySelector(
|
||||
@@ -1596,7 +1594,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.#animateFullOpen(browserRect);
|
||||
await this.#animateFullOpen();
|
||||
this.finishOpeningGlance();
|
||||
}
|
||||
|
||||
@@ -1632,31 +1630,27 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
|
||||
/**
|
||||
* Animate the full opening process
|
||||
*
|
||||
* @param {object} browserRect - The browser rectangle
|
||||
*/
|
||||
async #animateFullOpen(browserRect) {
|
||||
async #animateFullOpen() {
|
||||
// Write styles early to avoid flickering
|
||||
this.browserWrapper.style.opacity = 1;
|
||||
this.browserWrapper.style.width = `${browserRect.width}px`;
|
||||
this.browserWrapper.style.height = `${browserRect.height}px`;
|
||||
this.browserWrapper.style.width = "100%";
|
||||
this.browserWrapper.style.height = "100%";
|
||||
|
||||
await gZenUIManager.motion.animate(
|
||||
this.browserWrapper,
|
||||
await gZenUIManager.elementAnimate(
|
||||
this.browserWrapper.parentElement,
|
||||
{
|
||||
width: ["80%", "100%"],
|
||||
height: ["100%", "100%"],
|
||||
scale: [1, 1.005, 1],
|
||||
},
|
||||
{
|
||||
duration: this.#GLANCE_ANIMATION_DURATION,
|
||||
type: "spring",
|
||||
bounce: 0,
|
||||
duration: 250,
|
||||
easing: "ease-in-out",
|
||||
}
|
||||
);
|
||||
|
||||
this.browserWrapper.style.scale = "";
|
||||
this.browserWrapper.style.opacity = "";
|
||||
this.browserWrapper.style.width = "";
|
||||
this.browserWrapper.style.height = "";
|
||||
this.browserWrapper.style.opacity = "";
|
||||
gZenViewSplitter.deactivateCurrentSplitView({ removeDeckSelected: true });
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
display: flex;
|
||||
z-index: 999;
|
||||
|
||||
will-change: transform, opacity;
|
||||
contain: content;
|
||||
|
||||
top: 15px;
|
||||
padding: 12px;
|
||||
gap: 12px;
|
||||
@@ -121,6 +124,7 @@
|
||||
|
||||
&:not([has-finished-animation="true"]) {
|
||||
will-change: transform;
|
||||
contain: layout size;
|
||||
transform-origin: 0 0;
|
||||
|
||||
#statuspanel {
|
||||
|
||||
@@ -1602,7 +1602,6 @@ class nsZenWorkspaces {
|
||||
}
|
||||
|
||||
#prepareNewWorkspace(space) {
|
||||
document.documentElement.setAttribute("zen-workspace-id", space.uuid);
|
||||
let tabCount = 0;
|
||||
for (let tab of gBrowser.tabs) {
|
||||
const isEssential = tab.getAttribute("zen-essential") === "true";
|
||||
@@ -1643,12 +1642,12 @@ class nsZenWorkspaces {
|
||||
|
||||
async changeWorkspaceWithID(workspaceID, ...args) {
|
||||
const workspace = this.getWorkspaceFromId(workspaceID);
|
||||
await this.changeWorkspace(workspace, ...args);
|
||||
return await this.changeWorkspace(workspace, ...args);
|
||||
}
|
||||
|
||||
async changeWorkspace(workspace, ...args) {
|
||||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
return workspace;
|
||||
}
|
||||
this.#currentSpaceSwitchContext.animations.forEach(animation => {
|
||||
animation.complete();
|
||||
@@ -1668,6 +1667,7 @@ class nsZenWorkspaces {
|
||||
}
|
||||
this.#inChangingWorkspace = false;
|
||||
resolve();
|
||||
return workspace;
|
||||
}
|
||||
|
||||
_cancelSwipeAnimation() {
|
||||
@@ -2429,9 +2429,6 @@ class nsZenWorkspaces {
|
||||
tabToSelect,
|
||||
{ previousWorkspaceIndex, previousWorkspace } = {}
|
||||
) {
|
||||
// Update document state
|
||||
document.documentElement.setAttribute("zen-workspace-id", workspace.uuid);
|
||||
|
||||
// Recalculate new tab observers
|
||||
gBrowser.tabContainer.observe(
|
||||
null,
|
||||
@@ -2975,8 +2972,7 @@ class nsZenWorkspaces {
|
||||
}
|
||||
|
||||
let nextWorkspace = workspaces[targetIndex];
|
||||
await this.changeWorkspace(nextWorkspace, { whileScrolling });
|
||||
return nextWorkspace;
|
||||
return await this.changeWorkspace(nextWorkspace, { whileScrolling });
|
||||
}
|
||||
|
||||
#initializeWorkspaceTabContextMenus() {
|
||||
@@ -3000,9 +2996,8 @@ class nsZenWorkspaces {
|
||||
? gBrowser.selectedTabs
|
||||
: [TabContextMenu.contextTab];
|
||||
document.getElementById("tabContextMenu").hidePopup();
|
||||
const previousWorkspaceID =
|
||||
document.documentElement.getAttribute("zen-workspace-id");
|
||||
for (let tab of tabs) {
|
||||
const previousWorkspaceID = tab.getAttribute("zen-workspace-id");
|
||||
this.moveTabToWorkspace(tab, workspaceID);
|
||||
if (this.lastSelectedWorkspaceTabs[previousWorkspaceID] === tab) {
|
||||
// This tab is no longer the last selected tab in the previous workspace because it's being moved to
|
||||
|
||||
@@ -1239,13 +1239,24 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
|
||||
/**
|
||||
* Splits the selected tabs.
|
||||
*
|
||||
* @param {Tab|null} otherTabHint - An optional hint for another tab to split with (used for glance tabs).
|
||||
*/
|
||||
contextSplitTabs() {
|
||||
contextSplitTabs(otherTabHint = null) {
|
||||
let tabs;
|
||||
if (TabContextMenu.contextTab.multiselected) {
|
||||
let currentTab = TabContextMenu.contextTab || gBrowser.selectedTab;
|
||||
if (currentTab.multiselected) {
|
||||
tabs = gBrowser.selectedTabs;
|
||||
} else {
|
||||
tabs = [TabContextMenu.contextTab];
|
||||
tabs = [currentTab];
|
||||
}
|
||||
if (otherTabHint && !tabs.includes(otherTabHint)) {
|
||||
tabs.push(otherTabHint);
|
||||
}
|
||||
if (tabs.length < 2) {
|
||||
gBrowser.selectedTab = tabs[0];
|
||||
this.createEmptySplit();
|
||||
return;
|
||||
}
|
||||
// If all are already in a split view, we unsplit them first.
|
||||
if (tabs.every(tab => tab.splitView)) {
|
||||
@@ -1265,10 +1276,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
* @returns {boolean} True if the tabs can be split, false otherwise.
|
||||
*/
|
||||
contextCanSplitTabs() {
|
||||
if (
|
||||
window.gBrowser.selectedTabs.length < 2 ||
|
||||
window.gBrowser.selectedTabs.length > this.MAX_TABS
|
||||
) {
|
||||
if (window.gBrowser.selectedTabs.length > this.MAX_TABS) {
|
||||
return false;
|
||||
}
|
||||
for (const tab of window.gBrowser.selectedTabs) {
|
||||
@@ -1323,7 +1331,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
* @param {Array} tabs
|
||||
* @param {Tab} relativeTab
|
||||
*/
|
||||
_moveTabsToContainer(tabs, relativeTab) {
|
||||
#moveTabsToContainer(tabs, relativeTab) {
|
||||
const relativeTabIsPinned = relativeTab.pinned;
|
||||
const relativeTabIsEssential = relativeTab.hasAttribute("zen-essential");
|
||||
|
||||
@@ -1340,6 +1348,32 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
#useTabsToSplit(tabs) {
|
||||
// If there's ANY pinned tab on the list, we clone the pinned tab
|
||||
// state to all the tabs
|
||||
const allArePinned = tabs.every(tab => tab.pinned);
|
||||
const thereIsOnePinned = tabs.some(tab => tab.pinned);
|
||||
const thereIsOneEssential = tabs.some(tab =>
|
||||
tab.hasAttribute("zen-essential")
|
||||
);
|
||||
const thereIsOneLiveFolder = tabs.some(tab =>
|
||||
tab.hasAttribute("zen-live-folder-item-id")
|
||||
);
|
||||
|
||||
if (
|
||||
thereIsOneEssential ||
|
||||
(thereIsOnePinned && !allArePinned) ||
|
||||
thereIsOneLiveFolder
|
||||
) {
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
if (tab.pinned) {
|
||||
tabs[i] = gBrowser.duplicateTab(tab, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the given tabs.
|
||||
*
|
||||
@@ -1364,17 +1398,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
let shouldActivateSplit =
|
||||
(initialIndex >= 0 || tabs.includes(window.gBrowser.selectedTab)) &&
|
||||
!this._sessionRestoring;
|
||||
|
||||
if (existingSplitTab) {
|
||||
this._moveTabsToContainer(tabs, tabs[tabIndexToUse]);
|
||||
const groupIndex = this._data.findIndex(group =>
|
||||
group.tabs.includes(existingSplitTab)
|
||||
);
|
||||
const group = this._data[groupIndex];
|
||||
const gridTypeChange = gridType && group.gridType !== gridType;
|
||||
const newTabsAdded = tabs.find(t => !group.tabs.includes(t));
|
||||
if (group.tabs.length >= this.MAX_TABS) {
|
||||
gZenUIManager.showToast("zen-split-view-limit-toast");
|
||||
return;
|
||||
}
|
||||
this.#useTabsToSplit(tabs);
|
||||
this.#moveTabsToContainer(tabs, tabs[tabIndexToUse]);
|
||||
const gridTypeChange = gridType && group.gridType !== gridType;
|
||||
const newTabsAdded = tabs.find(t => !group.tabs.includes(t));
|
||||
if (gridTypeChange && !newTabsAdded) {
|
||||
// reset layout
|
||||
group.gridType = gridType;
|
||||
@@ -1406,30 +1443,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
return group;
|
||||
}
|
||||
|
||||
// We are here if none of the tabs have been previously split
|
||||
// If there's ANY pinned tab on the list, we clone the pinned tab
|
||||
// state to all the tabs
|
||||
const allArePinned = tabs.every(tab => tab.pinned);
|
||||
const thereIsOnePinned = tabs.some(tab => tab.pinned);
|
||||
const thereIsOneEssential = tabs.some(tab =>
|
||||
tab.hasAttribute("zen-essential")
|
||||
);
|
||||
const thereIsOneLiveFolder = tabs.some(tab =>
|
||||
tab.hasAttribute("zen-live-folder-item-id")
|
||||
);
|
||||
|
||||
if (
|
||||
thereIsOneEssential ||
|
||||
(thereIsOnePinned && !allArePinned) ||
|
||||
thereIsOneLiveFolder
|
||||
) {
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
if (tab.pinned) {
|
||||
tabs[i] = gBrowser.duplicateTab(tab, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.#useTabsToSplit(tabs);
|
||||
|
||||
gridType ??= "grid";
|
||||
|
||||
@@ -2172,7 +2186,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
splitGroup &&
|
||||
(!draggedTab.group || draggedTab.group !== splitGroup)
|
||||
) {
|
||||
this._moveTabsToContainer([draggedTab], droppedOnTab);
|
||||
this.#moveTabsToContainer([draggedTab], droppedOnTab);
|
||||
gBrowser.moveTabToExistingGroup(draggedTab, splitGroup);
|
||||
if (hoverSide === "left" || hoverSide === "top") {
|
||||
try {
|
||||
|
||||
@@ -857,7 +857,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0];
|
||||
const currentUrl = location.split("#")[0];
|
||||
// Add an indicator that the pin has been changed
|
||||
if (Services.io.newURI(currentUrl).spec === Services.io.newURI(pinUrl).spec) {
|
||||
if (
|
||||
Services.io.newURI(currentUrl).spec === Services.io.newURI(pinUrl).spec
|
||||
) {
|
||||
this.resetPinChangedUrl(tab);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -474,13 +474,13 @@
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
|
||||
:root[zen-workspace-id][zen-sidebar-expanded="true"] & {
|
||||
:root[zen-sidebar-expanded="true"] & {
|
||||
margin-left: calc(-1 * var(--zen-toolbox-padding));
|
||||
width: calc(100% + var(--zen-toolbox-padding) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
:root[zen-workspace-id] #pinned-tabs-container {
|
||||
#pinned-tabs-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,6 @@ add_task(async function test_Issue_10455() {
|
||||
|
||||
let newWindow = await BrowserTestUtils.openNewBrowserWindow();
|
||||
await newWindow.gZenWorkspaces.promiseInitialized;
|
||||
ok(
|
||||
newWindow.document.documentElement.hasAttribute("zen-workspace-id"),
|
||||
"New window should have a zen-workspace-id attribute"
|
||||
);
|
||||
|
||||
const unloadEvent = BrowserTestUtils.waitForEvent(newWindow, "unload");
|
||||
newWindow.BrowserCommands.closeTabOrWindow();
|
||||
@@ -33,10 +29,6 @@ add_task(async function test_Issue_10455_Dont_Close() {
|
||||
|
||||
let newWindow = await BrowserTestUtils.openNewBrowserWindow();
|
||||
await newWindow.gZenWorkspaces.promiseInitialized;
|
||||
ok(
|
||||
newWindow.document.documentElement.hasAttribute("zen-workspace-id"),
|
||||
"New window should have a zen-workspace-id attribute"
|
||||
);
|
||||
|
||||
newWindow.BrowserCommands.closeTabOrWindow();
|
||||
Assert.strictEqual(
|
||||
|
||||
@@ -12,10 +12,6 @@ add_task(async function test_Private_Mode() {
|
||||
private: true,
|
||||
});
|
||||
await privateWindow.gZenWorkspaces.promiseInitialized;
|
||||
ok(
|
||||
privateWindow.document.documentElement.hasAttribute("zen-workspace-id"),
|
||||
"Private window should have a zen-workspace-id attribute"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.closeWindow(privateWindow);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
|
||||
Reference in New Issue
Block a user