mirror of
https://github.com/zen-browser/desktop.git
synced 2025-09-29 14:38:37 +00:00
feat: Added empty splits support and more urlbar actions, b=no-bug, c=workspaces, common, kbs, split-view, tests
This commit is contained in:
@@ -311,6 +311,7 @@ zen-split-view-shortcut-grid = Toggle Split View Grid
|
|||||||
zen-split-view-shortcut-vertical = Toggle Split View Vertical
|
zen-split-view-shortcut-vertical = Toggle Split View Vertical
|
||||||
zen-split-view-shortcut-horizontal = Toggle Split View Horizontal
|
zen-split-view-shortcut-horizontal = Toggle Split View Horizontal
|
||||||
zen-split-view-shortcut-unsplit = Close Split View
|
zen-split-view-shortcut-unsplit = Close Split View
|
||||||
|
zen-new-empty-split-view-shortcut = New Empty Split View
|
||||||
zen-key-select-tab-1 = Select tab #1
|
zen-key-select-tab-1 = Select tab #1
|
||||||
zen-key-select-tab-2 = Select tab #2
|
zen-key-select-tab-2 = Select tab #2
|
||||||
zen-key-select-tab-3 = Select tab #3
|
zen-key-select-tab-3 = Select tab #3
|
||||||
|
@@ -7,6 +7,9 @@ zen-panel-ui-workspaces-create =
|
|||||||
zen-panel-ui-folder-create =
|
zen-panel-ui-folder-create =
|
||||||
.label = Create Folder
|
.label = Create Folder
|
||||||
|
|
||||||
|
zen-panel-ui-new-empty-split =
|
||||||
|
.label = New Split
|
||||||
|
|
||||||
zen-workspaces-panel-context-delete =
|
zen-workspaces-panel-context-delete =
|
||||||
.label = Delete Space
|
.label = Delete Space
|
||||||
.accesskey = D
|
.accesskey = D
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
<command id="cmd_zenSplitViewUnsplit" />
|
<command id="cmd_zenSplitViewUnsplit" />
|
||||||
<command id="cmd_zenSplitViewLinkInNewTab" />
|
<command id="cmd_zenSplitViewLinkInNewTab" />
|
||||||
<command id="cmd_zenSplitViewContextMenu" />
|
<command id="cmd_zenSplitViewContextMenu" />
|
||||||
|
<command id="cmd_zenNewEmptySplit" />
|
||||||
|
|
||||||
<!-- Workspace commands -->
|
<!-- Workspace commands -->
|
||||||
<command id="cmd_zenWorkspaceSwitch1" />
|
<command id="cmd_zenWorkspaceSwitch1" />
|
||||||
|
@@ -3,10 +3,11 @@
|
|||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
<menupopup id="zenCreateNewPopup">
|
<menupopup id="zenCreateNewPopup">
|
||||||
<menuitem data-l10n-id="tabs-toolbar-new-tab" command="cmd_newNavigatorTab" image="chrome://browser/skin/zen-icons/plus.svg" />
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem data-l10n-id="zen-panel-ui-folder-create" command="cmd_zenOpenFolderCreation" image="chrome://browser/skin/zen-icons/folder.svg" />
|
|
||||||
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation" image="chrome://browser/skin/zen-icons/duplicate-tab.svg" />
|
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation" image="chrome://browser/skin/zen-icons/duplicate-tab.svg" />
|
||||||
|
<menuitem data-l10n-id="zen-panel-ui-folder-create" command="cmd_zenOpenFolderCreation" image="chrome://browser/skin/zen-icons/folder.svg" />
|
||||||
|
<menuseparator/>
|
||||||
|
<menuitem data-l10n-id="zen-panel-ui-new-empty-split" command="cmd_zenNewEmptySplit" image="chrome://browser/skin/zen-icons/split.svg" />
|
||||||
|
<menuitem data-l10n-id="tabs-toolbar-new-tab" command="cmd_newNavigatorTab" image="chrome://browser/skin/zen-icons/plus.svg" />
|
||||||
</menupopup>
|
</menupopup>
|
||||||
|
|
||||||
<menupopup id="zenWorkspaceMoreActions">
|
<menupopup id="zenWorkspaceMoreActions">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010eedf8dcc29 100644
|
index 3204f253c23551650991d3385dd256d55892a012..e5a907a81526fde51087a0c33599fbb2948420ad 100644
|
||||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
@@ -427,15 +427,64 @@
|
@@ -427,15 +427,64 @@
|
||||||
@@ -395,10 +395,10 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
+ gZenWorkspaces._initialTab._shouldRemove = true;
|
+ gZenWorkspaces._initialTab._shouldRemove = true;
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ }
|
}
|
||||||
+ else {
|
+ else {
|
||||||
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
|
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
|
||||||
}
|
+ }
|
||||||
+ this._hasAlreadyInitializedZenSessionStore = true;
|
+ this._hasAlreadyInitializedZenSessionStore = true;
|
||||||
|
|
||||||
if (tabs.length > 1 || !tabs[0].selected) {
|
if (tabs.length > 1 || !tabs[0].selected) {
|
||||||
@@ -522,17 +522,22 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
if (
|
if (
|
||||||
!this._beginRemoveTab(aTab, {
|
!this._beginRemoveTab(aTab, {
|
||||||
closeWindowFastpath: true,
|
closeWindowFastpath: true,
|
||||||
@@ -4796,7 +4937,9 @@
|
@@ -4796,7 +4937,13 @@
|
||||||
// We're not animating, so we can cancel the animation stopwatch.
|
// We're not animating, so we can cancel the animation stopwatch.
|
||||||
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
||||||
aTab._closeTimeAnimTimerId = null;
|
aTab._closeTimeAnimTimerId = null;
|
||||||
+ gZenVerticalTabsManager.animateTabClose(aTab, (animate && !gReduceMotion)).then(() => {
|
- this._endRemoveTab(aTab);
|
||||||
this._endRemoveTab(aTab);
|
+ if (animate && !gReduceMotion && !gZenUIManager.testingEnabled) {
|
||||||
+ });
|
+ gZenVerticalTabsManager.animateTabClose(aTab, (animate && !gReduceMotion)).then(() => {
|
||||||
|
+ this._endRemoveTab(aTab);
|
||||||
|
+ });
|
||||||
|
+ } else {
|
||||||
|
+ this._endRemoveTab(aTab);
|
||||||
|
+ }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4930,7 +5073,7 @@
|
@@ -4930,7 +5077,7 @@
|
||||||
closeWindowWithLastTab != null
|
closeWindowWithLastTab != null
|
||||||
? closeWindowWithLastTab
|
? closeWindowWithLastTab
|
||||||
: !window.toolbar.visible ||
|
: !window.toolbar.visible ||
|
||||||
@@ -541,7 +546,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
|
|
||||||
if (closeWindow) {
|
if (closeWindow) {
|
||||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||||
@@ -4954,6 +5097,7 @@
|
@@ -4954,6 +5101,7 @@
|
||||||
|
|
||||||
newTab = true;
|
newTab = true;
|
||||||
}
|
}
|
||||||
@@ -549,7 +554,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||||
|
|
||||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||||
@@ -4994,13 +5138,7 @@
|
@@ -4994,13 +5142,7 @@
|
||||||
aTab._mouseleave();
|
aTab._mouseleave();
|
||||||
|
|
||||||
if (newTab) {
|
if (newTab) {
|
||||||
@@ -564,7 +569,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
} else {
|
} else {
|
||||||
TabBarVisibility.update();
|
TabBarVisibility.update();
|
||||||
}
|
}
|
||||||
@@ -5133,6 +5271,7 @@
|
@@ -5133,6 +5275,7 @@
|
||||||
this.tabs[i]._tPos = i;
|
this.tabs[i]._tPos = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,7 +577,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
if (!this._windowIsClosing) {
|
if (!this._windowIsClosing) {
|
||||||
// update tab close buttons state
|
// update tab close buttons state
|
||||||
this.tabContainer._updateCloseButtons();
|
this.tabContainer._updateCloseButtons();
|
||||||
@@ -5345,6 +5484,7 @@
|
@@ -5345,6 +5488,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let excludeTabs = new Set(aExcludeTabs);
|
let excludeTabs = new Set(aExcludeTabs);
|
||||||
@@ -580,7 +585,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
|
|
||||||
// If this tab has a successor, it should be selectable, since
|
// If this tab has a successor, it should be selectable, since
|
||||||
// hiding or closing a tab removes that tab as a successor.
|
// hiding or closing a tab removes that tab as a successor.
|
||||||
@@ -5357,13 +5497,13 @@
|
@@ -5357,13 +5501,13 @@
|
||||||
!excludeTabs.has(aTab.owner) &&
|
!excludeTabs.has(aTab.owner) &&
|
||||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||||
) {
|
) {
|
||||||
@@ -596,7 +601,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
);
|
);
|
||||||
|
|
||||||
let tab = this.tabContainer.findNextTab(aTab, {
|
let tab = this.tabContainer.findNextTab(aTab, {
|
||||||
@@ -5379,7 +5519,7 @@
|
@@ -5379,7 +5523,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab) {
|
if (tab) {
|
||||||
@@ -605,7 +610,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If no qualifying visible tab was found, see if there is a tab in
|
// If no qualifying visible tab was found, see if there is a tab in
|
||||||
@@ -5400,7 +5540,7 @@
|
@@ -5400,7 +5544,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,7 +619,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blurTab(aTab) {
|
_blurTab(aTab) {
|
||||||
@@ -5802,10 +5942,10 @@
|
@@ -5802,10 +5946,10 @@
|
||||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +632,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
aTab.selected ||
|
aTab.selected ||
|
||||||
aTab.closing ||
|
aTab.closing ||
|
||||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||||
@@ -5864,6 +6004,7 @@
|
@@ -5864,6 +6008,7 @@
|
||||||
* @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab
|
* @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab
|
||||||
*/
|
*/
|
||||||
replaceTabWithWindow(aTab, aOptions) {
|
replaceTabWithWindow(aTab, aOptions) {
|
||||||
@@ -635,7 +640,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
if (this.tabs.length == 1) {
|
if (this.tabs.length == 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -5997,7 +6138,7 @@
|
@@ -5997,7 +6142,7 @@
|
||||||
* `true` if element is a `<tab-group>`
|
* `true` if element is a `<tab-group>`
|
||||||
*/
|
*/
|
||||||
isTabGroup(element) {
|
isTabGroup(element) {
|
||||||
@@ -644,7 +649,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6073,8 +6214,8 @@
|
@@ -6073,8 +6218,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow mixing pinned and unpinned tabs.
|
// Don't allow mixing pinned and unpinned tabs.
|
||||||
@@ -655,7 +660,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
} else {
|
} else {
|
||||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||||
}
|
}
|
||||||
@@ -6100,10 +6241,16 @@
|
@@ -6100,10 +6245,16 @@
|
||||||
this.#handleTabMove(
|
this.#handleTabMove(
|
||||||
element,
|
element,
|
||||||
() => {
|
() => {
|
||||||
@@ -674,7 +679,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
||||||
neighbor.after(element);
|
neighbor.after(element);
|
||||||
} else {
|
} else {
|
||||||
@@ -6161,23 +6308,28 @@
|
@@ -6161,23 +6312,28 @@
|
||||||
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
||||||
if (this.isTabGroupLabel(targetElement)) {
|
if (this.isTabGroupLabel(targetElement)) {
|
||||||
targetElement = targetElement.group;
|
targetElement = targetElement.group;
|
||||||
@@ -709,7 +714,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||||
// If the caller asks to move an unpinned element next to a 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
|
// tab, move the unpinned element to be the first unpinned element
|
||||||
@@ -6190,14 +6342,34 @@
|
@@ -6190,14 +6346,34 @@
|
||||||
// move the tab group right before the first unpinned tab.
|
// move the tab group right before the first unpinned tab.
|
||||||
// 4. Moving a tab group and the first unpinned tab is grouped:
|
// 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.
|
// move the tab group right before the first unpinned tab's tab group.
|
||||||
@@ -745,7 +750,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
element.pinned
|
element.pinned
|
||||||
? this.tabContainer.pinnedTabsContainer
|
? this.tabContainer.pinnedTabsContainer
|
||||||
: this.tabContainer;
|
: this.tabContainer;
|
||||||
@@ -6206,7 +6378,7 @@
|
@@ -6206,7 +6382,7 @@
|
||||||
element,
|
element,
|
||||||
() => {
|
() => {
|
||||||
if (moveBefore) {
|
if (moveBefore) {
|
||||||
@@ -754,7 +759,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
} else if (targetElement) {
|
} else if (targetElement) {
|
||||||
targetElement.after(element);
|
targetElement.after(element);
|
||||||
} else {
|
} else {
|
||||||
@@ -6252,10 +6424,10 @@
|
@@ -6252,10 +6428,10 @@
|
||||||
* @param {TabMetricsContext} [metricsContext]
|
* @param {TabMetricsContext} [metricsContext]
|
||||||
*/
|
*/
|
||||||
moveTabToGroup(aTab, aGroup, metricsContext) {
|
moveTabToGroup(aTab, aGroup, metricsContext) {
|
||||||
@@ -767,7 +772,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||||
@@ -6285,6 +6457,7 @@
|
@@ -6285,6 +6461,7 @@
|
||||||
|
|
||||||
let state = {
|
let state = {
|
||||||
tabIndex: tab._tPos,
|
tabIndex: tab._tPos,
|
||||||
@@ -775,7 +780,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
};
|
};
|
||||||
if (tab.visible) {
|
if (tab.visible) {
|
||||||
state.elementIndex = tab.elementIndex;
|
state.elementIndex = tab.elementIndex;
|
||||||
@@ -6311,7 +6484,7 @@
|
@@ -6311,7 +6488,7 @@
|
||||||
let changedTabGroup =
|
let changedTabGroup =
|
||||||
previousTabState.tabGroupId != currentTabState.tabGroupId;
|
previousTabState.tabGroupId != currentTabState.tabGroupId;
|
||||||
|
|
||||||
@@ -784,7 +789,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
tab.dispatchEvent(
|
tab.dispatchEvent(
|
||||||
new CustomEvent("TabMove", {
|
new CustomEvent("TabMove", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -6348,6 +6521,10 @@
|
@@ -6348,6 +6525,10 @@
|
||||||
|
|
||||||
moveActionCallback();
|
moveActionCallback();
|
||||||
|
|
||||||
@@ -795,7 +800,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||||
// changed.
|
// changed.
|
||||||
this.tabContainer._invalidateCachedTabs();
|
this.tabContainer._invalidateCachedTabs();
|
||||||
@@ -7249,7 +7426,7 @@
|
@@ -7249,7 +7430,7 @@
|
||||||
// preventDefault(). It will still raise the window if appropriate.
|
// preventDefault(). It will still raise the window if appropriate.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -804,7 +809,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
window.focus();
|
window.focus();
|
||||||
aEvent.preventDefault();
|
aEvent.preventDefault();
|
||||||
break;
|
break;
|
||||||
@@ -7264,7 +7441,6 @@
|
@@ -7264,7 +7445,6 @@
|
||||||
}
|
}
|
||||||
case "TabGroupCollapse":
|
case "TabGroupCollapse":
|
||||||
aEvent.target.tabs.forEach(tab => {
|
aEvent.target.tabs.forEach(tab => {
|
||||||
@@ -812,7 +817,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "TabGroupCreateByUser":
|
case "TabGroupCreateByUser":
|
||||||
@@ -8199,6 +8375,7 @@
|
@@ -8199,6 +8379,7 @@
|
||||||
aWebProgress.isTopLevel
|
aWebProgress.isTopLevel
|
||||||
) {
|
) {
|
||||||
this.mTab.setAttribute("busy", "true");
|
this.mTab.setAttribute("busy", "true");
|
||||||
@@ -820,7 +825,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||||
}
|
}
|
||||||
@@ -9200,7 +9377,7 @@ var TabContextMenu = {
|
@@ -9200,7 +9381,7 @@ var TabContextMenu = {
|
||||||
);
|
);
|
||||||
contextUnpinSelectedTabs.hidden =
|
contextUnpinSelectedTabs.hidden =
|
||||||
!this.contextTab.pinned || !this.multiselected;
|
!this.contextTab.pinned || !this.multiselected;
|
||||||
@@ -829,7 +834,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
|||||||
// Build Ask Chat items
|
// Build Ask Chat items
|
||||||
TabContextMenu.GenAI.buildTabMenu(
|
TabContextMenu.GenAI.buildTabMenu(
|
||||||
document.getElementById("context_askChat"),
|
document.getElementById("context_askChat"),
|
||||||
@@ -9520,6 +9697,7 @@ var TabContextMenu = {
|
@@ -9520,6 +9701,7 @@ var TabContextMenu = {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs
|
diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs
|
||||||
index fdbab8806fd320f4aacec46a42c8ef953580d00c..40568280c3ba2f0a36f4443a5116430d3c502ec1 100644
|
index fdbab8806fd320f4aacec46a42c8ef953580d00c..5ed31d5dbfa2e2041e6616f6b036d67928e64114 100644
|
||||||
--- a/browser/components/urlbar/UrlbarView.sys.mjs
|
--- a/browser/components/urlbar/UrlbarView.sys.mjs
|
||||||
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
|
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
|
||||||
@@ -613,7 +613,7 @@ export class UrlbarView {
|
@@ -613,7 +613,7 @@ export class UrlbarView {
|
||||||
@@ -27,7 +27,7 @@ index fdbab8806fd320f4aacec46a42c8ef953580d00c..40568280c3ba2f0a36f4443a5116430d
|
|||||||
+ setAccessibleFocus:
|
+ setAccessibleFocus:
|
||||||
+ this.controller._userSelectionBehavior == "arrow",
|
+ this.controller._userSelectionBehavior == "arrow",
|
||||||
+ });
|
+ });
|
||||||
+ }, 150);
|
+ }, 140);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -111,7 +111,7 @@ export var ZenCustomizableUI = new (class {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const popup = window.document.getElementById('zenCreateNewPopup');
|
const popup = window.document.getElementById('zenCreateNewPopup');
|
||||||
popup.openPopup(button, 'after_start');
|
popup.openPopup(button, 'before_start');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -274,22 +274,23 @@ var gZenUIManager = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleNewTab(werePassedURL, searchClipboard, where) {
|
handleNewTab(werePassedURL, searchClipboard, where, overridePreferance = false) {
|
||||||
// Validate browser state first
|
// Validate browser state first
|
||||||
if (!this._validateBrowserState()) {
|
if (!this._validateBrowserState()) {
|
||||||
console.warn('Browser state invalid for new tab operation');
|
console.warn('Browser state invalid for new tab operation');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.testingEnabled) {
|
if (this.testingEnabled && !overridePreferance) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldOpenURLBar =
|
const shouldOpenURLBar =
|
||||||
gZenVerticalTabsManager._canReplaceNewTab &&
|
overridePreferance ||
|
||||||
!werePassedURL &&
|
(gZenVerticalTabsManager._canReplaceNewTab &&
|
||||||
!searchClipboard &&
|
!werePassedURL &&
|
||||||
where === 'tab';
|
!searchClipboard &&
|
||||||
|
where === 'tab');
|
||||||
|
|
||||||
if (!shouldOpenURLBar) {
|
if (!shouldOpenURLBar) {
|
||||||
return false;
|
return false;
|
||||||
@@ -399,7 +400,10 @@ var gZenUIManager = {
|
|||||||
|
|
||||||
if (gURLBar.focused) {
|
if (gURLBar.focused) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
gURLBar.view.close({ elementPicked: onSwitch });
|
window.dispatchEvent(
|
||||||
|
new CustomEvent('ZenURLBarClosed', { detail: { onSwitch, onElementPicked } })
|
||||||
|
);
|
||||||
|
gURLBar.view.close({ elementPicked: onElementPicked });
|
||||||
gURLBar.updateTextOverflow();
|
gURLBar.updateTextOverflow();
|
||||||
|
|
||||||
// Ensure tab and browser are valid before updating state
|
// Ensure tab and browser are valid before updating state
|
||||||
|
@@ -79,6 +79,11 @@ document.addEventListener(
|
|||||||
case 'cmd_zenSplitViewLinkInNewTab':
|
case 'cmd_zenSplitViewLinkInNewTab':
|
||||||
gZenViewSplitter.splitLinkInNewTab();
|
gZenViewSplitter.splitLinkInNewTab();
|
||||||
break;
|
break;
|
||||||
|
case 'cmd_zenNewEmptySplit':
|
||||||
|
setTimeout(() => {
|
||||||
|
gZenViewSplitter.createEmptySplit();
|
||||||
|
}, 0);
|
||||||
|
break;
|
||||||
case 'cmd_zenReplacePinnedUrlWithCurrent':
|
case 'cmd_zenReplacePinnedUrlWithCurrent':
|
||||||
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
|
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
|
||||||
break;
|
break;
|
||||||
|
@@ -816,7 +816,7 @@ class nsZenKeyboardShortcutsLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class nsZenKeyboardShortcutsVersioner {
|
class nsZenKeyboardShortcutsVersioner {
|
||||||
static LATEST_KBS_VERSION = 10;
|
static LATEST_KBS_VERSION = 11;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
@@ -1078,6 +1078,21 @@ class nsZenKeyboardShortcutsVersioner {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 11) {
|
||||||
|
// Migrate from version 10 to 11
|
||||||
|
data.push(
|
||||||
|
new KeyShortcut(
|
||||||
|
'zen-new-empty-split-view',
|
||||||
|
'+',
|
||||||
|
'',
|
||||||
|
ZEN_SPLIT_VIEW_SHORTCUTS_GROUP,
|
||||||
|
nsKeyShortcutModifiers.fromObject({ accel: true, alt: true }),
|
||||||
|
'cmd_zenNewEmptySplit',
|
||||||
|
'zen-new-empty-split-view-shortcut'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1008,6 +1008,15 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
|||||||
tab.linkedBrowser.docShellIsActive = true;
|
tab.linkedBrowser.docShellIsActive = true;
|
||||||
}
|
}
|
||||||
this._maybeRemoveFakeBrowser();
|
this._maybeRemoveFakeBrowser();
|
||||||
|
{
|
||||||
|
const shouldDisableEmptySplits = tab.hasAttribute('zen-empty-tab') || tab.splitView;
|
||||||
|
const command = document.getElementById('cmd_zenNewEmptySplit');
|
||||||
|
if (shouldDisableEmptySplits) {
|
||||||
|
command.setAttribute('disabled', 'true');
|
||||||
|
} else {
|
||||||
|
command.removeAttribute('disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1913,6 +1922,42 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createEmptySplit() {
|
||||||
|
const selectedTab = gBrowser.selectedTab;
|
||||||
|
const emptyTab = gZenWorkspaces._emptyTab;
|
||||||
|
const data = {
|
||||||
|
tabs: [selectedTab, emptyTab],
|
||||||
|
gridType: 'grid',
|
||||||
|
layoutTree: this.calculateLayoutTree([selectedTab, emptyTab], 'grid'),
|
||||||
|
};
|
||||||
|
this._data.push(data);
|
||||||
|
this.activateSplitView(data);
|
||||||
|
gBrowser.selectedTab = emptyTab;
|
||||||
|
window.addEventListener(
|
||||||
|
'ZenURLBarClosed',
|
||||||
|
(event) => {
|
||||||
|
const { onElementPicked } = event.detail;
|
||||||
|
const groupIndex = this._data.findIndex((group) => group.tabs.includes(emptyTab));
|
||||||
|
const newSelectedTab = gBrowser.selectedTab;
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.removeTabFromGroup(emptyTab, groupIndex);
|
||||||
|
if (onElementPicked) {
|
||||||
|
if (newSelectedTab === emptyTab || newSelectedTab === selectedTab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.splitTabs([selectedTab, newSelectedTab], 'grid', 1);
|
||||||
|
} else {
|
||||||
|
gBrowser.selectedTab = selectedTab;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
gZenUIManager.handleNewTab(false, false, 'tab', true);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.gZenViewSplitter = new nsZenViewSplitter();
|
window.gZenViewSplitter = new nsZenViewSplitter();
|
||||||
|
@@ -14,3 +14,4 @@ support-files = [
|
|||||||
["browser_split_browser_duplication.js"]
|
["browser_split_browser_duplication.js"]
|
||||||
["browser_split_view_with_glance.js"]
|
["browser_split_view_with_glance.js"]
|
||||||
["browser_split_view_with_folders.js"]
|
["browser_split_view_with_folders.js"]
|
||||||
|
["browser_split_view_empty.js"]
|
||||||
|
49
src/zen/tests/split_view/browser_split_view_empty.js
Normal file
49
src/zen/tests/split_view/browser_split_view_empty.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { UrlbarTestUtils } = ChromeUtils.importESModule(
|
||||||
|
'resource://testing-common/UrlbarTestUtils.sys.mjs'
|
||||||
|
);
|
||||||
|
|
||||||
|
add_task(async function test_Split_View_Empty() {
|
||||||
|
await BrowserTestUtils.withNewTab('https://example.com', async function () {
|
||||||
|
const originalTab = gBrowser.selectedTab;
|
||||||
|
const command = document.getElementById('cmd_zenNewEmptySplit');
|
||||||
|
command.doCommand();
|
||||||
|
await UrlbarTestUtils.promisePopupOpen(window, () => {});
|
||||||
|
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||||
|
window,
|
||||||
|
waitForFocus,
|
||||||
|
value: 'https://example.com',
|
||||||
|
});
|
||||||
|
const waitForActivationPromise = BrowserTestUtils.waitForEvent(
|
||||||
|
window,
|
||||||
|
'ZenViewSplitter:SplitViewActivated'
|
||||||
|
);
|
||||||
|
let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
|
||||||
|
EventUtils.synthesizeMouseAtCenter(result.element.row, {});
|
||||||
|
await waitForActivationPromise;
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(async () => {
|
||||||
|
resolve();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
const selectedTab = gBrowser.selectedTab;
|
||||||
|
ok(
|
||||||
|
gBrowser.tabpanels.hasAttribute('zen-split-view'),
|
||||||
|
'The split view should not have crashed with two tabs in it'
|
||||||
|
);
|
||||||
|
ok(!gZenWorkspaces._emptyTab.splitView, 'The empty tab should not be in split view');
|
||||||
|
ok(!gZenWorkspaces._emptyTab.group, 'The empty tab should not be in a group');
|
||||||
|
ok(selectedTab.splitView, 'The selected tab should be in split view');
|
||||||
|
ok(originalTab.splitView, 'The original tab should be in split view');
|
||||||
|
Assert.equal(
|
||||||
|
gBrowser.tabpanels.querySelectorAll('[zen-split="true"]').length,
|
||||||
|
2,
|
||||||
|
'There should be two split views present'
|
||||||
|
);
|
||||||
|
await BrowserTestUtils.removeTab(selectedTab);
|
||||||
|
});
|
||||||
|
});
|
@@ -64,21 +64,22 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {Window} window The window to check available actions for.
|
||||||
* @returns All the available global actions.
|
* @returns All the available global actions.
|
||||||
*/
|
*/
|
||||||
get #availableActions() {
|
#getAvailableActions(window) {
|
||||||
return globalActions.filter((a) =>
|
return globalActions.filter((a) => a.isAvailable(window));
|
||||||
typeof a.isAvailable === 'function' ? a.isAvailable() : true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a search query amongst the available global actions.
|
* Starts a search query amongst the available global actions.
|
||||||
*
|
*
|
||||||
* @param {string} queryContext The query context object
|
* @param {string} query The user's search query.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
#findMatchingActions(query) {
|
#findMatchingActions(query) {
|
||||||
const actions = this.#availableActions;
|
const window = lazy.BrowserWindowTracker.getTopWindow();
|
||||||
|
const actions = this.#getAvailableActions(window);
|
||||||
let results = [];
|
let results = [];
|
||||||
for (let action of actions) {
|
for (let action of actions) {
|
||||||
const label = action.label;
|
const label = action.label;
|
||||||
@@ -261,6 +262,10 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ownerGlobal = details.element.ownerGlobal;
|
const ownerGlobal = details.element.ownerGlobal;
|
||||||
|
if (typeof command === 'function') {
|
||||||
|
command(ownerGlobal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const commandToRun = ownerGlobal.document.getElementById(command);
|
const commandToRun = ownerGlobal.document.getElementById(command);
|
||||||
if (commandToRun) {
|
if (commandToRun) {
|
||||||
ownerGlobal.gBrowser.selectedBrowser.focus();
|
ownerGlobal.gBrowser.selectedBrowser.focus();
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
export const globalActions = [
|
const globalActionsTemplate = [
|
||||||
{
|
{
|
||||||
label: 'Toggle Compact Mode',
|
label: 'Toggle Compact Mode',
|
||||||
command: 'cmd_zenCompactModeToggle',
|
command: 'cmd_zenCompactModeToggle',
|
||||||
@@ -15,4 +15,33 @@ export const globalActions = [
|
|||||||
icon: 'chrome://browser/skin/zen-icons/edit-theme.svg',
|
icon: 'chrome://browser/skin/zen-icons/edit-theme.svg',
|
||||||
suggestedIndex: 4,
|
suggestedIndex: 4,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'New Split View',
|
||||||
|
command: 'cmd_zenNewEmptySplit',
|
||||||
|
icon: 'chrome://browser/skin/zen-icons/split.svg',
|
||||||
|
suggestedIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'New Folder',
|
||||||
|
command: 'cmd_zenOpenFolderCreation',
|
||||||
|
icon: 'chrome://browser/skin/zen-icons/folder.svg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Copy Current URL',
|
||||||
|
command: 'cmd_zenCopyCurrentURL',
|
||||||
|
icon: 'chrome://browser/skin/zen-icons/edit-copy.svg',
|
||||||
|
suggestedIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Settings',
|
||||||
|
command: (window) => window.openPreferences(),
|
||||||
|
icon: 'chrome://browser/skin/zen-icons/settings.svg',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const globalActions = globalActionsTemplate.map((action) => ({
|
||||||
|
...action,
|
||||||
|
isAvailable: (window) => {
|
||||||
|
return window.document.getElementById(action.command)?.getAttribute('disabled') !== 'true';
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
Reference in New Issue
Block a user