feat(sidebar): part 2 - tree vertical tabs v1.1
This commit is contained in:
@@ -6,6 +6,10 @@
|
||||
<box context="sidebar-context-menu" id="sidebar-main" hidden="true">
|
||||
<html:sidebar-main flex="1">
|
||||
<box id="vertical-tabs" slot="tabstrip" customizable="true" contextmenu="toolbar-context-menu"></box>
|
||||
<box id="tree-vertical-tabs-box"
|
||||
slot="tabstrip"
|
||||
orient="vertical"
|
||||
hidden="true"/>
|
||||
</html:sidebar-main>
|
||||
</box>
|
||||
<splitter id="sidebar-launcher-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
|
||||
|
||||
@@ -1510,6 +1510,8 @@ var PlacesControllerDragHelper = {
|
||||
* @returns {string} The most relevant flavor, or undefined.
|
||||
*/
|
||||
getMostRelevantFlavor(flavors) {
|
||||
if (flavors.contains(PlacesUtils.TYPE_X_WS_TREE))
|
||||
return PlacesUtils.TYPE_X_WS_TREE;
|
||||
// The DnD API returns a DOMStringList, but tests may pass an Array.
|
||||
flavors = Array.from(flavors);
|
||||
return PlacesUIUtils.SUPPORTED_FLAVORS.find(f => flavors.includes(f));
|
||||
@@ -1526,6 +1528,8 @@ var PlacesControllerDragHelper = {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canDrop: function PCDH_canDrop(ip, dt) {
|
||||
if (dt.mozTypesAt(0).contains(PlacesUtils.TYPE_X_WS_TREE))
|
||||
return true;
|
||||
let dropCount = dt.mozItemCount;
|
||||
|
||||
// Check every dragged item.
|
||||
|
||||
@@ -580,6 +580,165 @@ var SidebarController = {
|
||||
);
|
||||
},
|
||||
|
||||
_toggleTreeInProgress: false,
|
||||
|
||||
/**
|
||||
* Toggle the vertical tabs preference.
|
||||
*/
|
||||
async toggleTreeVerticalTabs(shouldShow) {
|
||||
// Using a lock to prevent re-entrancy from the preference observer
|
||||
// while the function is already being executed from the UI event.
|
||||
if (this._toggleTreeInProgress) {
|
||||
return;
|
||||
}
|
||||
this._toggleTreeInProgress = true;
|
||||
|
||||
try {
|
||||
const treeVerticalTabsBox = document.querySelector(
|
||||
"#tree-vertical-tabs-box"
|
||||
);
|
||||
const verticalTabs = document.querySelector("#vertical-tabs");
|
||||
shouldShow ??= !this.treeVerticalTabsBrowser;
|
||||
const addon = await AddonManager.getAddonByID("sidebar@waterfox.net");
|
||||
if (shouldShow != this.treeVerticalTabsEnabled) {
|
||||
Services.prefs.setBoolPref("browser.sidebar.enabled", shouldShow);
|
||||
}
|
||||
if (shouldShow) {
|
||||
if (this.treeVerticalTabsBrowser) {
|
||||
return;
|
||||
}
|
||||
if (!addon.isActive) {
|
||||
await addon.enable({ allowSystemAddons: true });
|
||||
}
|
||||
await addon.startupPromise;
|
||||
if (!Services.prefs.getBoolPref("sidebar.revamp")) {
|
||||
await this.toggleRevampSidebar();
|
||||
}
|
||||
if (!Services.prefs.getBoolPref("sidebar.verticalTabs")) {
|
||||
Services.prefs.setBoolPref("sidebar.verticalTabs", true);
|
||||
// The sidebar-main maybe not initialized yet at the first time.
|
||||
while (!this.sidebarMain.requestUpdate) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
this.toggleTabstrip();
|
||||
}
|
||||
|
||||
treeVerticalTabsBox.removeAttribute("hidden");
|
||||
treeVerticalTabsBox.setAttribute("shown", true);
|
||||
|
||||
verticalTabs.setAttribute("hidden", true);
|
||||
verticalTabs.removeAttribute("visible");
|
||||
|
||||
const policy = WebExtensionPolicy.getByID("sidebar@waterfox.net");
|
||||
|
||||
const treeVerticalTabs = document.createXULElement("browser");
|
||||
treeVerticalTabs.setAttribute("id", "tree-vertical-tabs");
|
||||
treeVerticalTabs.setAttribute("type", "content");
|
||||
treeVerticalTabs.setAttribute("flex", "1");
|
||||
treeVerticalTabs.setAttribute("disableglobalhistory", "true");
|
||||
treeVerticalTabs.setAttribute(
|
||||
"messagemanagergroup",
|
||||
"webext-browsers"
|
||||
);
|
||||
treeVerticalTabs.setAttribute("webextension-view-type", "sidebar");
|
||||
treeVerticalTabs.setAttribute("context", "contentAreaContextMenu");
|
||||
treeVerticalTabs.setAttribute("tooltip", "aHTMLTooltip");
|
||||
treeVerticalTabs.setAttribute("autocompletepopup", "PopupAutoComplete");
|
||||
treeVerticalTabs.setAttribute("transparent", "true");
|
||||
treeVerticalTabs.setAttribute("remote", "true");
|
||||
treeVerticalTabs.setAttribute("remoteType", "extension");
|
||||
treeVerticalTabs.setAttribute("maychangeremoteness", "true");
|
||||
treeVerticalTabs.setAttribute("remoteType", "extension");
|
||||
treeVerticalTabs.setAttribute("transparent", "true");
|
||||
treeVerticalTabs.setAttribute(
|
||||
"initialBrowsingContextGroupId",
|
||||
policy.browsingContextGroupId
|
||||
);
|
||||
|
||||
const { ExtensionUtils } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/ExtensionUtils.sys.mjs"
|
||||
);
|
||||
const { promiseEvent } = ExtensionUtils;
|
||||
const initBrowser = () => {
|
||||
const { ExtensionParent } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/ExtensionParent.sys.mjs"
|
||||
);
|
||||
ExtensionParent.apiManager.emit(
|
||||
"extension-browser-inserted",
|
||||
treeVerticalTabs,
|
||||
{}
|
||||
);
|
||||
treeVerticalTabs.messageManager.loadFrameScript(
|
||||
"chrome://extensions/content/ext-browser-content.js",
|
||||
false,
|
||||
true
|
||||
);
|
||||
treeVerticalTabs.messageManager.sendAsyncMessage(
|
||||
"Extension:InitBrowser",
|
||||
{}
|
||||
);
|
||||
|
||||
const sidebarPanelUrl = policy.getURL("sidebar/sidebar.html");
|
||||
const uri = Services.io.newURI(sidebarPanelUrl);
|
||||
const triggeringPrincipal =
|
||||
Services.scriptSecurityManager.createContentPrincipal(uri, {});
|
||||
treeVerticalTabs.fixupAndLoadURIString(sidebarPanelUrl, {
|
||||
triggeringPrincipal,
|
||||
});
|
||||
};
|
||||
promiseEvent(treeVerticalTabs, "XULFrameLoaderCreated").then(
|
||||
initBrowser
|
||||
);
|
||||
treeVerticalTabs.addEventListener(
|
||||
"DidChangeBrowserRemoteness",
|
||||
initBrowser
|
||||
);
|
||||
|
||||
treeVerticalTabsBox.appendChild(treeVerticalTabs);
|
||||
|
||||
const event = new CustomEvent("TreeVerticalTabsShown", {
|
||||
bubbles: true,
|
||||
});
|
||||
treeVerticalTabsBox.dispatchEvent(event);
|
||||
} else {
|
||||
const treeVerticalTabs = this.treeVerticalTabsBrowser;
|
||||
if (!treeVerticalTabs) {
|
||||
return;
|
||||
}
|
||||
const uri = Services.io.newURI("about:blank");
|
||||
const triggeringPrincipal =
|
||||
Services.scriptSecurityManager.createContentPrincipal(uri, {});
|
||||
treeVerticalTabs.fixupAndLoadURIString("about:blank", {
|
||||
triggeringPrincipal,
|
||||
});
|
||||
treeVerticalTabsBox.removeChild(treeVerticalTabs);
|
||||
|
||||
treeVerticalTabsBox.setAttribute("hidden", true);
|
||||
treeVerticalTabsBox.removeAttribute("shown");
|
||||
|
||||
verticalTabs.removeAttribute("hidden");
|
||||
if (Services.prefs.getBoolPref("sidebar.verticalTabs")) {
|
||||
verticalTabs.setAttribute("visible", "");
|
||||
}
|
||||
|
||||
const event = new CustomEvent("TreeVerticalTabsHidden", {
|
||||
bubbles: true,
|
||||
});
|
||||
treeVerticalTabsBox.dispatchEvent(event);
|
||||
|
||||
if (!shouldShow && addon.isActive) {
|
||||
await addon.disable({ allowSystemAddons: true });
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this._toggleTreeInProgress = false;
|
||||
}
|
||||
},
|
||||
|
||||
get treeVerticalTabsBrowser() {
|
||||
return document.querySelector("#tree-vertical-tabs");
|
||||
},
|
||||
|
||||
/**
|
||||
* The handler for Services.obs.addObserver.
|
||||
*/
|
||||
@@ -760,6 +919,7 @@ var SidebarController = {
|
||||
if (content && content.updatePosition) {
|
||||
content.updatePosition();
|
||||
}
|
||||
this.treeVerticalTabsBrowser?.reload();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -857,6 +1017,10 @@ var SidebarController = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.treeVerticalTabsEnabled) {
|
||||
this.toggleTreeVerticalTabs();
|
||||
}
|
||||
|
||||
let sourceWindow = window.opener;
|
||||
// No source window means this is the initial window. If we're being
|
||||
// opened from another window, check that it is one we might open a sidebar
|
||||
@@ -1979,6 +2143,9 @@ var SidebarController = {
|
||||
arrowScrollbox.setAttribute("orient", "horizontal");
|
||||
tabStrip.removeAttribute("expanded");
|
||||
tabStrip.setAttribute("orient", "horizontal");
|
||||
if (this.treeVerticalTabsEnabled) {
|
||||
this.toggleTreeVerticalTabs(false);
|
||||
}
|
||||
}
|
||||
|
||||
let verticalToolbar = document.getElementById(
|
||||
@@ -2307,3 +2474,13 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
SidebarController,
|
||||
"treeVerticalTabsEnabled",
|
||||
"browser.sidebar.enabled",
|
||||
false,
|
||||
(_aPreference, _previousValue, newValue) => {
|
||||
SidebarController.toggleTreeVerticalTabs(newValue);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<title data-l10n-id="sidebar-customize-title"></title>
|
||||
<link rel="localization" href="branding/brand.ftl" />
|
||||
<link rel="localization" href="browser/sidebar.ftl" />
|
||||
<link rel="localization" href="browser/waterfox.ftl" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="chrome://browser/content/sidebar/sidebar.css"
|
||||
|
||||
@@ -296,6 +296,15 @@ export class SidebarCustomize extends SidebarPage {
|
||||
?disabled=${this.getWindow().SidebarController._state
|
||||
.revampVisibility === "expand-on-hover"}
|
||||
></moz-checkbox>
|
||||
<moz-checkbox
|
||||
slot="nested"
|
||||
type="checkbox"
|
||||
id="tree-vertical-tabs"
|
||||
name="treeVerticalTabs"
|
||||
data-l10n-id="sidebar-tree-vertical-tabs"
|
||||
@change=${this.#handleTreeVerticalTabsChange}
|
||||
?checked=${this.getWindow().SidebarController.treeVerticalTabsEnabled}
|
||||
></moz-checkbox>
|
||||
`
|
||||
)}
|
||||
</moz-checkbox>
|
||||
@@ -356,6 +365,11 @@ export class SidebarCustomize extends SidebarPage {
|
||||
});
|
||||
}
|
||||
|
||||
#handleTreeVerticalTabsChange(e) {
|
||||
e.stopPropagation();
|
||||
this.getWindow().SidebarController.toggleTreeVerticalTabs(e.target.checked);
|
||||
}
|
||||
|
||||
#toggleExpandOnHover(e) {
|
||||
e.stopPropagation();
|
||||
if (e.target.checked) {
|
||||
|
||||
@@ -132,6 +132,9 @@ export default class SidebarMain extends MozLitElement {
|
||||
}
|
||||
|
||||
onSidebarPopupShowing(event) {
|
||||
if (event.target == SidebarController.treeVerticalTabsBrowser) {
|
||||
return;
|
||||
}
|
||||
// Store the context menu target which holds the id required for managing sidebar items
|
||||
let targetHost = event.explicitOriginalTarget.getRootNode().host;
|
||||
let toolbarContextMenuTarget =
|
||||
|
||||
@@ -62,8 +62,15 @@ export default class TabHoverPreviewPanel {
|
||||
this._panelOpener = new TabPreviewPanelTimedFunction(
|
||||
() => {
|
||||
if (!this._isDisabled()) {
|
||||
if (this._win.SidebarController.treeVerticalTabsEnabled &&
|
||||
typeof this.__ws__top == 'number') {
|
||||
const [x, y] = this.calculateCoordinatesForVertical();
|
||||
this._panel.openPopupAtScreen(x, y, false);
|
||||
}
|
||||
else {
|
||||
this._panel.openPopup(this._tab, this.#popupOptions);
|
||||
}
|
||||
}
|
||||
},
|
||||
this._prefPreviewDelay,
|
||||
ZERO_DELAY_ACTIVATION_TIME,
|
||||
@@ -165,6 +172,18 @@ export default class TabHoverPreviewPanel {
|
||||
});
|
||||
}
|
||||
|
||||
calculateCoordinatesForVertical() {
|
||||
const treeVerticalTabs = this._win.document.querySelector('#tree-vertical-tabs');
|
||||
const treeVerticalTabsRect = treeVerticalTabs.getBoundingClientRect();
|
||||
const align = Services.prefs.getBoolPref('sidebar.position_start') ? 'left' : 'right';
|
||||
const previewRect = this._panel.getBoundingClientRect();
|
||||
const x = align == 'left' ?
|
||||
treeVerticalTabs.screenX + treeVerticalTabsRect.width - 2 :
|
||||
treeVerticalTabs.screenX - previewRect.width + 2;
|
||||
const y = Math.min(treeVerticalTabs.screenY + this.__ws__top, treeVerticalTabs.screenY + treeVerticalTabsRect.height - previewRect.height);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
activate(tab) {
|
||||
if (this._isDisabled()) {
|
||||
return;
|
||||
@@ -270,6 +289,12 @@ export default class TabHoverPreviewPanel {
|
||||
|
||||
_movePanel() {
|
||||
if (this._tab) {
|
||||
if (this._win.SidebarController.treeVerticalTabsEnabled &&
|
||||
typeof this.__ws__top == 'number') {
|
||||
const [x, y] = this.calculateCoordinatesForVertical();
|
||||
this._panel.moveTo(x, y);
|
||||
return;
|
||||
}
|
||||
this._panel.moveToAnchor(
|
||||
this._tab,
|
||||
this.#popupOptions.position,
|
||||
|
||||
@@ -430,6 +430,9 @@ export var PlacesUtils = {
|
||||
// Used to track the action that populated the clipboard.
|
||||
TYPE_X_MOZ_PLACE_ACTION: "text/x-moz-place-action",
|
||||
|
||||
// Used to track actouns from the Tree Vertical Tabs sidebar.
|
||||
TYPE_X_WS_TREE: "application/x-ws-tree",
|
||||
|
||||
// Deprecated: Remaining only for supporting migration of old livemarks.
|
||||
LMANNO_FEEDURI: "livemark/feedURI",
|
||||
LMANNO_SITEURI: "livemark/siteURI",
|
||||
@@ -1305,6 +1308,17 @@ export var PlacesUtils = {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case this.TYPE_X_WS_TREE: {
|
||||
const data = JSON.parse(blob);
|
||||
for (const tab of data.tabs) {
|
||||
validNodes.push({
|
||||
uri: tab.url,
|
||||
title: tab.title,
|
||||
type: "text/x-moz-url",
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user