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-horizontal = Toggle Split View Horizontal
|
||||
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-2 = Select tab #2
|
||||
zen-key-select-tab-3 = Select tab #3
|
||||
|
@@ -7,6 +7,9 @@ zen-panel-ui-workspaces-create =
|
||||
zen-panel-ui-folder-create =
|
||||
.label = Create Folder
|
||||
|
||||
zen-panel-ui-new-empty-split =
|
||||
.label = New Split
|
||||
|
||||
zen-workspaces-panel-context-delete =
|
||||
.label = Delete Space
|
||||
.accesskey = D
|
||||
|
@@ -19,6 +19,7 @@
|
||||
<command id="cmd_zenSplitViewUnsplit" />
|
||||
<command id="cmd_zenSplitViewLinkInNewTab" />
|
||||
<command id="cmd_zenSplitViewContextMenu" />
|
||||
<command id="cmd_zenNewEmptySplit" />
|
||||
|
||||
<!-- Workspace commands -->
|
||||
<command id="cmd_zenWorkspaceSwitch1" />
|
||||
|
@@ -3,10 +3,11 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
<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-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 id="zenWorkspaceMoreActions">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -427,15 +427,64 @@
|
||||
@@ -395,10 +395,10 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
+ gZenWorkspaces._initialTab._shouldRemove = true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ else {
|
||||
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
|
||||
}
|
||||
+ }
|
||||
+ this._hasAlreadyInitializedZenSessionStore = true;
|
||||
|
||||
if (tabs.length > 1 || !tabs[0].selected) {
|
||||
@@ -522,17 +522,22 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
if (
|
||||
!this._beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -4796,7 +4937,9 @@
|
||||
@@ -4796,7 +4937,13 @@
|
||||
// We're not animating, so we can cancel the animation stopwatch.
|
||||
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4930,7 +5073,7 @@
|
||||
@@ -4930,7 +5077,7 @@
|
||||
closeWindowWithLastTab != null
|
||||
? closeWindowWithLastTab
|
||||
: !window.toolbar.visible ||
|
||||
@@ -541,7 +546,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
|
||||
if (closeWindow) {
|
||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||
@@ -4954,6 +5097,7 @@
|
||||
@@ -4954,6 +5101,7 @@
|
||||
|
||||
newTab = true;
|
||||
}
|
||||
@@ -549,7 +554,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||
|
||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||
@@ -4994,13 +5138,7 @@
|
||||
@@ -4994,13 +5142,7 @@
|
||||
aTab._mouseleave();
|
||||
|
||||
if (newTab) {
|
||||
@@ -564,7 +569,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
} else {
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -5133,6 +5271,7 @@
|
||||
@@ -5133,6 +5275,7 @@
|
||||
this.tabs[i]._tPos = i;
|
||||
}
|
||||
|
||||
@@ -572,7 +577,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
if (!this._windowIsClosing) {
|
||||
// update tab close buttons state
|
||||
this.tabContainer._updateCloseButtons();
|
||||
@@ -5345,6 +5484,7 @@
|
||||
@@ -5345,6 +5488,7 @@
|
||||
}
|
||||
|
||||
let excludeTabs = new Set(aExcludeTabs);
|
||||
@@ -580,7 +585,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
|
||||
// If this tab has a successor, it should be selectable, since
|
||||
// hiding or closing a tab removes that tab as a successor.
|
||||
@@ -5357,13 +5497,13 @@
|
||||
@@ -5357,13 +5501,13 @@
|
||||
!excludeTabs.has(aTab.owner) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||
) {
|
||||
@@ -596,7 +601,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
);
|
||||
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
@@ -5379,7 +5519,7 @@
|
||||
@@ -5379,7 +5523,7 @@
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
@@ -605,7 +610,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -5802,10 +5942,10 @@
|
||||
@@ -5802,10 +5946,10 @@
|
||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||
}
|
||||
|
||||
@@ -627,7 +632,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// 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
|
||||
*/
|
||||
replaceTabWithWindow(aTab, aOptions) {
|
||||
@@ -635,7 +640,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
if (this.tabs.length == 1) {
|
||||
return null;
|
||||
}
|
||||
@@ -5997,7 +6138,7 @@
|
||||
@@ -5997,7 +6142,7 @@
|
||||
* `true` if element is a `<tab-group>`
|
||||
*/
|
||||
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.
|
||||
@@ -655,7 +660,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
} else {
|
||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||
}
|
||||
@@ -6100,10 +6241,16 @@
|
||||
@@ -6100,10 +6245,16 @@
|
||||
this.#handleTabMove(
|
||||
element,
|
||||
() => {
|
||||
@@ -674,7 +679,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
||||
neighbor.after(element);
|
||||
} else {
|
||||
@@ -6161,23 +6308,28 @@
|
||||
@@ -6161,23 +6312,28 @@
|
||||
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
||||
if (this.isTabGroupLabel(targetElement)) {
|
||||
targetElement = targetElement.group;
|
||||
@@ -709,7 +714,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
} 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
|
||||
@@ -6190,14 +6342,34 @@
|
||||
@@ -6190,14 +6346,34 @@
|
||||
// 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.
|
||||
@@ -745,7 +750,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
element.pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -6206,7 +6378,7 @@
|
||||
@@ -6206,7 +6382,7 @@
|
||||
element,
|
||||
() => {
|
||||
if (moveBefore) {
|
||||
@@ -754,7 +759,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
} else if (targetElement) {
|
||||
targetElement.after(element);
|
||||
} else {
|
||||
@@ -6252,10 +6424,10 @@
|
||||
@@ -6252,10 +6428,10 @@
|
||||
* @param {TabMetricsContext} [metricsContext]
|
||||
*/
|
||||
moveTabToGroup(aTab, aGroup, metricsContext) {
|
||||
@@ -767,7 +772,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
return;
|
||||
}
|
||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||
@@ -6285,6 +6457,7 @@
|
||||
@@ -6285,6 +6461,7 @@
|
||||
|
||||
let state = {
|
||||
tabIndex: tab._tPos,
|
||||
@@ -775,7 +780,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
};
|
||||
if (tab.visible) {
|
||||
state.elementIndex = tab.elementIndex;
|
||||
@@ -6311,7 +6484,7 @@
|
||||
@@ -6311,7 +6488,7 @@
|
||||
let changedTabGroup =
|
||||
previousTabState.tabGroupId != currentTabState.tabGroupId;
|
||||
|
||||
@@ -784,7 +789,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
tab.dispatchEvent(
|
||||
new CustomEvent("TabMove", {
|
||||
bubbles: true,
|
||||
@@ -6348,6 +6521,10 @@
|
||||
@@ -6348,6 +6525,10 @@
|
||||
|
||||
moveActionCallback();
|
||||
|
||||
@@ -795,7 +800,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||
// changed.
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
@@ -7249,7 +7426,7 @@
|
||||
@@ -7249,7 +7430,7 @@
|
||||
// preventDefault(). It will still raise the window if appropriate.
|
||||
break;
|
||||
}
|
||||
@@ -804,7 +809,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
window.focus();
|
||||
aEvent.preventDefault();
|
||||
break;
|
||||
@@ -7264,7 +7441,6 @@
|
||||
@@ -7264,7 +7445,6 @@
|
||||
}
|
||||
case "TabGroupCollapse":
|
||||
aEvent.target.tabs.forEach(tab => {
|
||||
@@ -812,7 +817,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
});
|
||||
break;
|
||||
case "TabGroupCreateByUser":
|
||||
@@ -8199,6 +8375,7 @@
|
||||
@@ -8199,6 +8379,7 @@
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this.mTab.setAttribute("busy", "true");
|
||||
@@ -820,7 +825,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||
}
|
||||
@@ -9200,7 +9377,7 @@ var TabContextMenu = {
|
||||
@@ -9200,7 +9381,7 @@ var TabContextMenu = {
|
||||
);
|
||||
contextUnpinSelectedTabs.hidden =
|
||||
!this.contextTab.pinned || !this.multiselected;
|
||||
@@ -829,7 +834,7 @@ index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010ee
|
||||
// Build Ask Chat items
|
||||
TabContextMenu.GenAI.buildTabMenu(
|
||||
document.getElementById("context_askChat"),
|
||||
@@ -9520,6 +9697,7 @@ var TabContextMenu = {
|
||||
@@ -9520,6 +9701,7 @@ var TabContextMenu = {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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
|
||||
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
|
||||
@@ -613,7 +613,7 @@ export class UrlbarView {
|
||||
@@ -27,7 +27,7 @@ index fdbab8806fd320f4aacec46a42c8ef953580d00c..40568280c3ba2f0a36f4443a5116430d
|
||||
+ setAccessibleFocus:
|
||||
+ this.controller._userSelectionBehavior == "arrow",
|
||||
+ });
|
||||
+ }, 150);
|
||||
+ }, 140);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -111,7 +111,7 @@ export var ZenCustomizableUI = new (class {
|
||||
return;
|
||||
}
|
||||
const popup = window.document.getElementById('zenCreateNewPopup');
|
||||
popup.openPopup(button, 'after_start');
|
||||
popup.openPopup(button, 'before_start');
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -274,22 +274,23 @@ var gZenUIManager = {
|
||||
return true;
|
||||
},
|
||||
|
||||
handleNewTab(werePassedURL, searchClipboard, where) {
|
||||
handleNewTab(werePassedURL, searchClipboard, where, overridePreferance = false) {
|
||||
// Validate browser state first
|
||||
if (!this._validateBrowserState()) {
|
||||
console.warn('Browser state invalid for new tab operation');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.testingEnabled) {
|
||||
if (this.testingEnabled && !overridePreferance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const shouldOpenURLBar =
|
||||
gZenVerticalTabsManager._canReplaceNewTab &&
|
||||
!werePassedURL &&
|
||||
!searchClipboard &&
|
||||
where === 'tab';
|
||||
overridePreferance ||
|
||||
(gZenVerticalTabsManager._canReplaceNewTab &&
|
||||
!werePassedURL &&
|
||||
!searchClipboard &&
|
||||
where === 'tab');
|
||||
|
||||
if (!shouldOpenURLBar) {
|
||||
return false;
|
||||
@@ -399,7 +400,10 @@ var gZenUIManager = {
|
||||
|
||||
if (gURLBar.focused) {
|
||||
setTimeout(() => {
|
||||
gURLBar.view.close({ elementPicked: onSwitch });
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('ZenURLBarClosed', { detail: { onSwitch, onElementPicked } })
|
||||
);
|
||||
gURLBar.view.close({ elementPicked: onElementPicked });
|
||||
gURLBar.updateTextOverflow();
|
||||
|
||||
// Ensure tab and browser are valid before updating state
|
||||
|
@@ -79,6 +79,11 @@ document.addEventListener(
|
||||
case 'cmd_zenSplitViewLinkInNewTab':
|
||||
gZenViewSplitter.splitLinkInNewTab();
|
||||
break;
|
||||
case 'cmd_zenNewEmptySplit':
|
||||
setTimeout(() => {
|
||||
gZenViewSplitter.createEmptySplit();
|
||||
}, 0);
|
||||
break;
|
||||
case 'cmd_zenReplacePinnedUrlWithCurrent':
|
||||
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
|
||||
break;
|
||||
|
@@ -816,7 +816,7 @@ class nsZenKeyboardShortcutsLoader {
|
||||
}
|
||||
|
||||
class nsZenKeyboardShortcutsVersioner {
|
||||
static LATEST_KBS_VERSION = 10;
|
||||
static LATEST_KBS_VERSION = 11;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -1008,6 +1008,15 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
tab.linkedBrowser.docShellIsActive = true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
@@ -14,3 +14,4 @@ support-files = [
|
||||
["browser_split_browser_duplication.js"]
|
||||
["browser_split_view_with_glance.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.
|
||||
*/
|
||||
get #availableActions() {
|
||||
return globalActions.filter((a) =>
|
||||
typeof a.isAvailable === 'function' ? a.isAvailable() : true
|
||||
);
|
||||
#getAvailableActions(window) {
|
||||
return globalActions.filter((a) => a.isAvailable(window));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
const actions = this.#availableActions;
|
||||
const window = lazy.BrowserWindowTracker.getTopWindow();
|
||||
const actions = this.#getAvailableActions(window);
|
||||
let results = [];
|
||||
for (let action of actions) {
|
||||
const label = action.label;
|
||||
@@ -261,6 +262,10 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
return;
|
||||
}
|
||||
const ownerGlobal = details.element.ownerGlobal;
|
||||
if (typeof command === 'function') {
|
||||
command(ownerGlobal);
|
||||
return;
|
||||
}
|
||||
const commandToRun = ownerGlobal.document.getElementById(command);
|
||||
if (commandToRun) {
|
||||
ownerGlobal.gBrowser.selectedBrowser.focus();
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
export const globalActions = [
|
||||
const globalActionsTemplate = [
|
||||
{
|
||||
label: 'Toggle Compact Mode',
|
||||
command: 'cmd_zenCompactModeToggle',
|
||||
@@ -15,4 +15,33 @@ export const globalActions = [
|
||||
icon: 'chrome://browser/skin/zen-icons/edit-theme.svg',
|
||||
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