gecko-dev/devtools/client/framework/menu.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

201 строка
5.2 KiB
JavaScript
Исходник Обычный вид История

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
Bug 1461522 - Use the currentZoom as opposed to the pref value; r=jdescottes In a couple of places in DevTools we read back the value of the "devtools.toolbox.zoomValue" pref and take that to be the zoom value. However, at certain zoom levels these two can differ: the pref value representing the ideal zoom and currentZoom giving the actual zoom value in use. This patch makes us use the actual zoom value in the two places where this occurs. Unfortunately, we cannot easily adjust the browser_html_tooltip_zoom.js test for this since it uses a separate XUL document that does not take into account the pref value---as a result that test directly sets the currentZoom on the separate doc and hence this problem won't occur. Instead, this patch adjusts the browser_toolbox_zoom_popup.js test since the toolbox menu positioning uses the same problematic pattern so we can reproduce the bug in the browser_toolbox_zoom_popup.js. (This patch fixes both occurances of this pattern.) At least locally browser_toolbox_zoom_popup.js passes for me with a zoom of 1.5 but fails with a zoom of 1.4. Similarly in my testing of HTMLTooltip, zoom values such as 1.2 and 1.4 often show significant misalignment whilst a zoom of 1.3 or 1.5 is fine. With the code changes in this patch, the test passes with any given zoom factor. (This patch also incidentally replaced isNaN with the more robust Number.isNaN.) MozReview-Commit-ID: JmlRoidARVp --HG-- extra : rebase_source : 39370089e38c473987c9bfb1a4ba582cc781db1b
2018-06-28 08:55:48 +03:00
const { getCurrentZoom } = require("devtools/shared/layout/utils");
/**
* A partial implementation of the Menu API provided by electron:
* https://github.com/electron/electron/blob/master/docs/api/menu.md.
*
* Extra features:
* - Emits an 'open' and 'close' event when the menu is opened/closed
* @param String id (non standard)
* Needed so tests can confirm the XUL implementation is working
*/
function Menu({ id = null } = {}) {
this.menuitems = [];
this.id = id;
Object.defineProperty(this, "items", {
get() {
return this.menuitems;
},
});
EventEmitter.decorate(this);
}
/**
* Add an item to the end of the Menu
*
* @param {MenuItem} menuItem
*/
Menu.prototype.append = function(menuItem) {
this.menuitems.push(menuItem);
};
/**
* Add an item to a specified position in the menu
*
* @param {int} pos
* @param {MenuItem} menuItem
*/
Menu.prototype.insert = function(pos, menuItem) {
throw Error("Not implemented");
};
/**
* Show the Menu with anchor element's coordinate.
* For example, In the case of zoom in/out the devtool panel, we should multiply
* element's position to zoom value.
* If you know the screen coodinate of display position, you should use Menu.pop().
*
* @param {int} x
* @param {int} y
* @param Toolbox toolbox
*/
Menu.prototype.popupWithZoom = function(x, y, toolbox) {
Bug 1461522 - Use the currentZoom as opposed to the pref value; r=jdescottes In a couple of places in DevTools we read back the value of the "devtools.toolbox.zoomValue" pref and take that to be the zoom value. However, at certain zoom levels these two can differ: the pref value representing the ideal zoom and currentZoom giving the actual zoom value in use. This patch makes us use the actual zoom value in the two places where this occurs. Unfortunately, we cannot easily adjust the browser_html_tooltip_zoom.js test for this since it uses a separate XUL document that does not take into account the pref value---as a result that test directly sets the currentZoom on the separate doc and hence this problem won't occur. Instead, this patch adjusts the browser_toolbox_zoom_popup.js test since the toolbox menu positioning uses the same problematic pattern so we can reproduce the bug in the browser_toolbox_zoom_popup.js. (This patch fixes both occurances of this pattern.) At least locally browser_toolbox_zoom_popup.js passes for me with a zoom of 1.5 but fails with a zoom of 1.4. Similarly in my testing of HTMLTooltip, zoom values such as 1.2 and 1.4 often show significant misalignment whilst a zoom of 1.3 or 1.5 is fine. With the code changes in this patch, the test passes with any given zoom factor. (This patch also incidentally replaced isNaN with the more robust Number.isNaN.) MozReview-Commit-ID: JmlRoidARVp --HG-- extra : rebase_source : 39370089e38c473987c9bfb1a4ba582cc781db1b
2018-06-28 08:55:48 +03:00
const zoom = getCurrentZoom(toolbox.doc);
this.popup(x * zoom, y * zoom, toolbox);
};
/**
* Show the Menu at a specified location on the screen
*
* Missing features:
* - browserWindow - BrowserWindow (optional) - Default is null.
* - positioningItem Number - (optional) OS X
*
* @param {int} screenX
* @param {int} screenY
* @param Toolbox toolbox (non standard)
* Needed so we in which window to inject XUL
*/
Menu.prototype.popup = function(screenX, screenY, toolbox) {
const doc = toolbox.doc;
let popupset = doc.querySelector("popupset");
if (!popupset) {
popupset = doc.createXULElement("popupset");
doc.documentElement.appendChild(popupset);
}
// See bug 1285229, on Windows, opening the same popup multiple times in a
// row ends up duplicating the popup. The newly inserted popup doesn't
// dismiss the old one. So remove any previously displayed popup before
// opening a new one.
let popup = popupset.querySelector("menupopup[menu-api=\"true\"]");
if (popup) {
popup.hidePopup();
}
popup = doc.createXULElement("menupopup");
popup.setAttribute("menu-api", "true");
popup.setAttribute("consumeoutsideclicks", "true");
if (this.id) {
popup.id = this.id;
}
this._createMenuItems(popup);
// Remove the menu from the DOM once it's hidden.
popup.addEventListener("popuphidden", (e) => {
if (e.target === popup) {
popup.remove();
this.emit("close");
}
});
popup.addEventListener("popupshown", (e) => {
if (e.target === popup) {
this.emit("open");
}
});
popupset.appendChild(popup);
popup.openPopupAtScreen(screenX, screenY, true);
};
Menu.prototype._createMenuItems = function(parent) {
const doc = parent.ownerDocument;
this.menuitems.forEach(item => {
if (!item.visible) {
return;
}
if (item.submenu) {
const menupopup = doc.createXULElement("menupopup");
item.submenu._createMenuItems(menupopup);
const menu = doc.createXULElement("menu");
menu.appendChild(menupopup);
applyItemAttributesToNode(item, menu);
parent.appendChild(menu);
} else if (item.type === "separator") {
const menusep = doc.createXULElement("menuseparator");
parent.appendChild(menusep);
} else {
const menuitem = doc.createXULElement("menuitem");
applyItemAttributesToNode(item, menuitem);
menuitem.addEventListener("command", () => {
item.click();
});
menuitem.addEventListener("DOMMenuItemActive", () => {
item.hover();
});
parent.appendChild(menuitem);
}
});
};
Menu.setApplicationMenu = () => {
throw Error("Not implemented");
};
Menu.sendActionToFirstResponder = () => {
throw Error("Not implemented");
};
Menu.buildFromTemplate = () => {
throw Error("Not implemented");
};
function applyItemAttributesToNode(item, node) {
if (item.l10nID) {
node.setAttribute("data-l10n-id", item.l10nID);
} else {
node.setAttribute("label", item.label);
if (item.accelerator) {
node.setAttribute("acceltext", item.accelerator);
}
if (item.accesskey) {
node.setAttribute("accesskey", item.accesskey);
}
}
if (item.type === "checkbox") {
node.setAttribute("type", "checkbox");
}
if (item.type === "radio") {
node.setAttribute("type", "radio");
}
if (item.disabled) {
node.setAttribute("disabled", "true");
}
if (item.checked) {
node.setAttribute("checked", "true");
}
if (item.id) {
node.id = item.id;
}
}
module.exports = Menu;