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">
|
<box context="sidebar-context-menu" id="sidebar-main" hidden="true">
|
||||||
<html:sidebar-main flex="1">
|
<html:sidebar-main flex="1">
|
||||||
<box id="vertical-tabs" slot="tabstrip" customizable="true" contextmenu="toolbar-context-menu"></box>
|
<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>
|
</html:sidebar-main>
|
||||||
</box>
|
</box>
|
||||||
<splitter id="sidebar-launcher-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
|
<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.
|
* @returns {string} The most relevant flavor, or undefined.
|
||||||
*/
|
*/
|
||||||
getMostRelevantFlavor(flavors) {
|
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.
|
// The DnD API returns a DOMStringList, but tests may pass an Array.
|
||||||
flavors = Array.from(flavors);
|
flavors = Array.from(flavors);
|
||||||
return PlacesUIUtils.SUPPORTED_FLAVORS.find(f => flavors.includes(f));
|
return PlacesUIUtils.SUPPORTED_FLAVORS.find(f => flavors.includes(f));
|
||||||
@@ -1526,6 +1528,8 @@ var PlacesControllerDragHelper = {
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
canDrop: function PCDH_canDrop(ip, dt) {
|
canDrop: function PCDH_canDrop(ip, dt) {
|
||||||
|
if (dt.mozTypesAt(0).contains(PlacesUtils.TYPE_X_WS_TREE))
|
||||||
|
return true;
|
||||||
let dropCount = dt.mozItemCount;
|
let dropCount = dt.mozItemCount;
|
||||||
|
|
||||||
// Check every dragged item.
|
// 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.
|
* The handler for Services.obs.addObserver.
|
||||||
*/
|
*/
|
||||||
@@ -760,6 +919,7 @@ var SidebarController = {
|
|||||||
if (content && content.updatePosition) {
|
if (content && content.updatePosition) {
|
||||||
content.updatePosition();
|
content.updatePosition();
|
||||||
}
|
}
|
||||||
|
this.treeVerticalTabsBrowser?.reload();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -857,6 +1017,10 @@ var SidebarController = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.treeVerticalTabsEnabled) {
|
||||||
|
this.toggleTreeVerticalTabs();
|
||||||
|
}
|
||||||
|
|
||||||
let sourceWindow = window.opener;
|
let sourceWindow = window.opener;
|
||||||
// No source window means this is the initial window. If we're being
|
// 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
|
// opened from another window, check that it is one we might open a sidebar
|
||||||
@@ -1979,6 +2143,9 @@ var SidebarController = {
|
|||||||
arrowScrollbox.setAttribute("orient", "horizontal");
|
arrowScrollbox.setAttribute("orient", "horizontal");
|
||||||
tabStrip.removeAttribute("expanded");
|
tabStrip.removeAttribute("expanded");
|
||||||
tabStrip.setAttribute("orient", "horizontal");
|
tabStrip.setAttribute("orient", "horizontal");
|
||||||
|
if (this.treeVerticalTabsEnabled) {
|
||||||
|
this.toggleTreeVerticalTabs(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let verticalToolbar = document.getElementById(
|
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>
|
<title data-l10n-id="sidebar-customize-title"></title>
|
||||||
<link rel="localization" href="branding/brand.ftl" />
|
<link rel="localization" href="branding/brand.ftl" />
|
||||||
<link rel="localization" href="browser/sidebar.ftl" />
|
<link rel="localization" href="browser/sidebar.ftl" />
|
||||||
|
<link rel="localization" href="browser/waterfox.ftl" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="chrome://browser/content/sidebar/sidebar.css"
|
href="chrome://browser/content/sidebar/sidebar.css"
|
||||||
|
|||||||
@@ -296,6 +296,15 @@ export class SidebarCustomize extends SidebarPage {
|
|||||||
?disabled=${this.getWindow().SidebarController._state
|
?disabled=${this.getWindow().SidebarController._state
|
||||||
.revampVisibility === "expand-on-hover"}
|
.revampVisibility === "expand-on-hover"}
|
||||||
></moz-checkbox>
|
></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>
|
</moz-checkbox>
|
||||||
@@ -356,6 +365,11 @@ export class SidebarCustomize extends SidebarPage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#handleTreeVerticalTabsChange(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.getWindow().SidebarController.toggleTreeVerticalTabs(e.target.checked);
|
||||||
|
}
|
||||||
|
|
||||||
#toggleExpandOnHover(e) {
|
#toggleExpandOnHover(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ export default class SidebarMain extends MozLitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSidebarPopupShowing(event) {
|
onSidebarPopupShowing(event) {
|
||||||
|
if (event.target == SidebarController.treeVerticalTabsBrowser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Store the context menu target which holds the id required for managing sidebar items
|
// Store the context menu target which holds the id required for managing sidebar items
|
||||||
let targetHost = event.explicitOriginalTarget.getRootNode().host;
|
let targetHost = event.explicitOriginalTarget.getRootNode().host;
|
||||||
let toolbarContextMenuTarget =
|
let toolbarContextMenuTarget =
|
||||||
|
|||||||
@@ -62,8 +62,15 @@ export default class TabHoverPreviewPanel {
|
|||||||
this._panelOpener = new TabPreviewPanelTimedFunction(
|
this._panelOpener = new TabPreviewPanelTimedFunction(
|
||||||
() => {
|
() => {
|
||||||
if (!this._isDisabled()) {
|
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._panel.openPopup(this._tab, this.#popupOptions);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
this._prefPreviewDelay,
|
this._prefPreviewDelay,
|
||||||
ZERO_DELAY_ACTIVATION_TIME,
|
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) {
|
activate(tab) {
|
||||||
if (this._isDisabled()) {
|
if (this._isDisabled()) {
|
||||||
return;
|
return;
|
||||||
@@ -270,6 +289,12 @@ export default class TabHoverPreviewPanel {
|
|||||||
|
|
||||||
_movePanel() {
|
_movePanel() {
|
||||||
if (this._tab) {
|
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._panel.moveToAnchor(
|
||||||
this._tab,
|
this._tab,
|
||||||
this.#popupOptions.position,
|
this.#popupOptions.position,
|
||||||
|
|||||||
@@ -430,6 +430,9 @@ export var PlacesUtils = {
|
|||||||
// Used to track the action that populated the clipboard.
|
// Used to track the action that populated the clipboard.
|
||||||
TYPE_X_MOZ_PLACE_ACTION: "text/x-moz-place-action",
|
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.
|
// Deprecated: Remaining only for supporting migration of old livemarks.
|
||||||
LMANNO_FEEDURI: "livemark/feedURI",
|
LMANNO_FEEDURI: "livemark/feedURI",
|
||||||
LMANNO_SITEURI: "livemark/siteURI",
|
LMANNO_SITEURI: "livemark/siteURI",
|
||||||
@@ -1305,6 +1308,17 @@ export var PlacesUtils = {
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user