一般的 UI cmets
使用右键单击直接激活您的功能与系统范围内使用的一般 UI 是相反的。右键单击在系统范围内(在某些系统上)用于打开上下文菜单。在 Firefox 的工具栏中,这用于显示工具栏区域的上下文菜单。这是您的用户在使用右键单击时通常期望发生的情况。您最好使用 shift-left-click 之类的东西,或者让用户定义要用于激活您的功能的组合。如果您正在尝试向上下文菜单添加一个选项,那么通常可以通过右键单击访问该选项。
其他插件中使用的替代品:
使用右键单击
问题似乎在于 Add-on SDK ActionButton 已经抽象出您拥有任意事件侦听器的能力。它也不允许您访问通常传递给事件处理程序(侦听器)的实际event object。此外,它的click 事件似乎实际上是command event,而不是click event。 click 和 command 事件之间的显着区别之一是 command 事件通常不会在右键单击时触发。
您将需要访问 ActionButton 界面之外的按钮,并为 click 事件添加一个侦听器,然后在您的点击事件处理程序中,您可以根据状态选择执行您的操作event.button 和 event.shiftKey。
根据我发布为 an answer for a different question 的内容调整一些代码,你会想要类似的东西(未经修改测试):
function loadUi(buttonId) {
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
forEachCustomizableUiById(buttonId, loadIntoButton, window);
}
function forEachCustomizableUiById(buttonId ,func, myWindow) {
let groupWidgetWrap = myWindow.CustomizableUI.getWidget(buttonId);
groupWidgetWrap.instances.forEach(function(windowUiWidget) {
//For each button do the load task.
func(windowUiWidget.node);
});
}
function loadIntoButton(buttonElement) {
//Make whatever changes to the button you want to here.
//You may need to save some information about the original state
// of the button.
buttonElement.addEventListener("click",handleClickEvent,true);
}
function unloadUi(buttonId) {
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
forEachCustomizableUiById(buttonId, unloadFromButton, window);
}
function unloadFromButton(buttonElement) {
//Return the button to its original state
buttonElement.removeEventListener("click",handleClickEvent,true);
}
function handleClickEvent(event) {
If( (event.button & 2) == 2 && event.shiftKey){
event.preventDefault();
event.stopPropagation();
doMyThing();
}
}
function doMyThing() {
//Whatever it is that you are going to do.
}
正如上面代码所暗示的那样,您需要确保在卸载/禁用附加组件时删除您的侦听器。您还需要确保在打开新窗口时调用loadUi(),以便将您的处理程序添加到新按钮中。
添加到上下文菜单
没有直接的方法可以只为您的图标更改上下文菜单。上下文菜单的 ID 是 toolbar-context-menu。您可以做的是将项目添加到通常为hidden="true" 的上下文菜单中。当您收到在您的图标上发生右键单击的事件时,您可以为您添加的那些项目更改hidden 的状态。然后在上下文菜单 (<menupopup id="toolbar-context-menu">) 的 popuphidden 事件上调用的事件处理程序中,您可以为已添加到 @987654358 的 <menuitem> 元素设置 hidden="true" 的状态@ 在每个浏览器窗口中。
类似的东西:
function loadIntoContextMenu(win){
let doc = win.ownerDocument;
let contextPopupEl = doc.getElementById("toolbar-context-menu");
contextPopupEl.insertAdjacentHTML("beforeend",
'<menuitem label="My Item A" id="myExtensionPrefix-context-itemA"'
+ ' oncommand="doMyThingA();" hidden="true" />'
+ '<menuitem label="My Item B" id="myExtensionPrefix-context-itemB"'
+ ' oncommand="doMyThingB();" hidden="true" />');
contextPopupEl.addEventListener("popuphidden",hideMyContextMenuItems,true);
}
function unloadFromContextMenu(win){
let doc = win.ownerDocument;
let contextPopupEl = doc.getElementById("toolbar-context-menu");
let itemA = doc.getElementById("myExtensionPrefix-context-itemA");
let itemB = doc.getElementById("myExtensionPrefix-context-itemB");
contextPopupEl.removeChild(itemA);
contextPopupEl.removeChild(itemB);
contextPopupEl.removeEventListener("popuphidden",hideContextMenuItems,true);
}
function setHiddenMyContextMenuItems(element,text){
//The element is the context menu.
//text is what you want the "hidden" attribute to be set to.
let child = element.firstChild;
while(child !== null){
if(/myExtensionPrefix-context-item[AB]/.test(child.id)){
child.setAttribute("hidden",text);
}
child = child.nextSibling;
}
}
function showContextMenuItems(event){
//The target of this event is the button for which you want to change the
// context menu. We need to find the context menu element.
let contextmenuEl = event.target.ownerDocument
.getElementById("toolbar-context-menu");
setHiddenMyContextMenuItems(contextmenuEl,"false");
}
function hideContextMenuItems(event){
//This is called for the popuphidden event of the context menu.
setHiddenMyContextMenuItems(event.target,"true");
}
//Change the handleClickEvent function in the code within the earlier section:
function handleClickEvent(event) {
If( (event.button & 2) == 2){
//don't prevent propagation, nor the default as the context menu
// showing is desired.
showContextMenuItems(event);
}
}
同样,我还没有测试过这个。它应该展示一种实现您想要的方式的方法。
但是,鉴于我们正在讨论上下文菜单,使用contextmenu 事件可能比click 事件和测试右键单击更好。在这种情况下,我们会将上面的一些函数更改为:
function loadIntoButton(buttonElement) {
//Make whatever changes to the button you want to here.
buttonElement.addEventListener("contextmenu",handleContextmenuEvent,true);
}
function handleContextmenuEvent(event) {
showContextMenuItems(event);
}
您可以通过使用nsIWindowMediator 获取每个打开的主浏览器窗口。以下函数 from MDN 将为每个打开的窗口运行一次您传递给它的函数:
Components.utils.import("resource://gre/modules/Services.jsm");
function forEachOpenWindow(todo) // Apply a function to all open browser windows
{
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
}
在附加 SDK 中:
function forEachOpenWindow(todo) // Apply a function to all open browser windows
var windows = require("sdk/windows");
for (let window of windows.browserWindows) {
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
}
您可以使用以下代码(也称为from MDN)为新窗口添加一个调用loadIntoContextMenu 的侦听器:
Components.utils.import("resource://gre/modules/Services.jsm");
Services.wm.addListener(WindowListener);
var WindowListener =
{
onOpenWindow: function(xulWindow)
{
var window = xulWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
function onWindowLoad()
{
window.removeEventListener("load",onWindowLoad);
if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser"){
loadIntoContextMenu(window);
//It would be better to only do this for the current window, but
// it does not hurt to do it to all of them again.
loadUi(buttonId);
}
}
window.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { },
onWindowTitleChange: function(xulWindow, newTitle) { }
};