美好的一天!
我再次搜索和搜索,我没有找到任何帮助来解决这个问题......
背景: 我开发了一个非常简单的Google Chrome扩展程序:只需点击一下即可向某人发送电子邮件。要配置此选项,此扩展程序上有一个选项页面,用于设置要发送的电子邮件地址。 My Google Chrome extension is available here(English translation,只是文字,而不是安装)。
用户已要求我为Firefox制作此扩展程序,以便我正在处理它!
我已经阅读了有关cfx的教程,没关系。但我需要让我的扩展程序响应右键单击工具栏中的扩展程序图标。不在页面上,在我的图标上。
我使用ActionButton(或ToggleButton)将我的图标添加到工具栏中,但我找不到在右键单击上添加菜单的方法(那里已经是默认的Firefox上下文菜单,但我想添加"选项"。)
如果有人有解决方案,那就太好了!
(我不熟悉XUL。所以,如果可能的话,只有一个JavaScript解决方案,请^^)
PS:我是法国人,请原谅我的英语不好
编辑:我已经找到了如何在我的" package.json"中设置首选项。文件,但我想要自己的窗口。 如果我们可以"绑定"按钮"选项"在附加管理器到我自己的窗口,这将是完美的!编辑2:因为对每个人都不清楚,我会在这里详细说明我对扩展的要求: - 在图标上单击(左键单击)获取当前URL并将其发送到邮件地址(确定) - 只需点击即可。这个扩展旨在非常简单! - 右键单击图标显示Firefox的上下文菜单,我想添加"选项"这里显示我的选项页面 - 插件管理器可以有一个"选项"按钮靠近"停用"和"调试"我想要这个选项按钮来显示我的选项页面。 => 2种查看我的选项页面的方法:通过右键单击或插件管理器,这就是我需要你帮助的原因!
答案 0 :(得分:3)
使用右键单击直接激活您的功能与系统范围内使用的常规UI相反。右键单击是系统范围的(在某些系统上),用于打开上下文菜单。在工具栏的Firefox中,这用于显示工具栏区域的上下文菜单。这是用户在使用右键单击时通常会发生的情况。你可能最好使用shift-left-click之类的东西,或者让用户定义用于激活你的功能的组合。如果您正在尝试向上下文菜单添加选项,则通常可以通过右键单击来访问该选项。
其他附加组件中使用的替代方案:
<tooltip id="myTooltip"></tooltip>
元素中,并使用<button tooltip="myTooltip"/>
(tooltip property,tooltip attribute)在按钮中引用它。问题似乎是附加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
事件中调用的事件处理程序中,您可以为<menuitem>
元素设置hidden="true"
的状态您已添加到每个浏览器窗口中的<menupopup id="toolbar-context-menu">
。
有些事情:
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) { }
};
答案 1 :(得分:2)
我已经实施了一个具有主要和辅助操作的menu-button
。虽然它不是右/左点击,但按钮有两面:
这允许您将两个不同的操作与按钮相关联,而不会改变Firefox的常用上下文菜单流。下载文件on GitHub并将其存储在lib folder
。
用法类似于其他按钮类型。在main.js
(或lib
目录中的任何js文件)中包含以下代码
const { MenuButton } = require('./menu-button');
var btn = MenuButton({
id: 'my-menu-button',
label: 'My menu-button',
icon: {
"16": "./firefox-16.png",
"32": "./firefox-32.png"
},
onClick: click
});
click
函数将与state
和toggle
按钮传递相同的action
对象,并将传递一个额外的布尔参数:isMenu
。它应该像这样使用
function click(state, isMenu) {
if (isMenu) {
//menu-button clicked
} else {
//icon clicked
}
}
答案 2 :(得分:1)
在回答完这个问题之后,我在Chrome上尝试了你的扩展,看到我的答案可能不是你想要的,所以我会提出不同的建议(留下另一个答案,因为我觉得它有用人们在一个按钮上寻找多个动作。)
我要说的一件事是(某些)Chrome用户知道Options
菜单项指的是扩展名而不是浏览器选项。这些用户知道菜单项在那里,并使用它来更改其扩展设置。 Firefox用户不希望出现这种情况,因为上下文菜单操作都会影响浏览器,而不会影响扩展。以同样的方式,(某些)Firefox用户知道要更改其扩展程序设置,他们必须导航到about:addons
(或工具/插件),然后点击扩展程序旁边的Preferences
按钮。这是改变您的偏好的预期途径。所以我认为添加上下文菜单选项非常复杂,并不是一个好的解决方案。
相反,如果您的用户尚未设置自己的偏好设置,我认为您应该在Chrome中执行以下操作:创建Panel
,将其与您的按钮相关联(使用position: button
面板构造函数),并告诉用户他们需要通过导航到工具/插件来设置他们的首选项。如果您使用simple prefs
模块,则扩展程序旁边会显示Preferences
按钮,您在package.json
中设置的选项也可以在此处进行修改。
不幸的是,这是一个非常基本的页面,看起来不像你做的漂亮的HTML选项页面。
Bonne chance。
答案 3 :(得分:1)
除了其他答案中的所有保留以及不重用现有工具栏上下文菜单之外,还有以下方法:
const utils = require('sdk/window/utils');
const window = utils.getMostRecentBrowserWindow();
const doc = window.document;
const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
const { getNodeView } = require("sdk/view/core");
let ButContext = doc.createElementNS(XUL_NS,'menupopup');
ButContext.setAttribute("id","customIDString");
menuitem = doc.createElementNS(XUL_NS,"menuitem");
menuitem.setAttribute("label", "customLabelString");
ButContext.appendChild(item);
var myBut = require("sdk/ui/button/action").ActionButton({
id: "myButton",
label: "Right click me!"
// And other options
});
//Here is the context magic
getNodeView(myBut).setAttribute('context', "customIDString");
//either ; gets removed together with the button
getNodeView(myBut).appendChild(ButContext);
//or ; the correct place but needs to be removed manually
doc.getElementById("mainPopupSet").appendChild(ButContext);