Bug 1461522 - Add a mechanism to allow updating an HTMLTooltip's size and position; r=jdescottes

MozReview-Commit-ID: 4SDxlTTFp8E

--HG--
extra : rebase_source : c8e429c8d88512bc807a3169b6651609e01e5556
This commit is contained in:
Brian Birtles 2018-06-28 15:13:05 +09:00
parent 1b60bc9ae5
commit 59e52349bd
3 changed files with 134 additions and 24 deletions

View File

@ -145,6 +145,7 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
[browser_html_tooltip_height-auto.js]
[browser_html_tooltip_hover.js]
[browser_html_tooltip_offset.js]
[browser_html_tooltip_resize.js]
[browser_html_tooltip_rtl.js]
[browser_html_tooltip_variable-height.js]
[browser_html_tooltip_width-auto.js]

View File

@ -0,0 +1,77 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_html_tooltip.js */
"use strict";
/**
* Test the HTMLTooltip can be resized.
*/
const HTML_NS = "http://www.w3.org/1999/xhtml";
const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xul";
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
const TOOLBOX_WIDTH = 500;
add_task(async function() {
await pushPref("devtools.toolbox.sidebar.width", TOOLBOX_WIDTH);
// Open the host on the right so that the doorhangers hang right.
const [,, doc] = await createHost("right", TEST_URI);
info("Test resizing of a tooltip");
const tooltip =
new HTMLTooltip(doc, { useXulWrapper: true, type: "doorhanger" });
const div = doc.createElementNS(HTML_NS, "div");
div.textContent = "tooltip";
div.style.cssText = "width: 100px; height: 40px";
tooltip.setContent(div);
const box1 = doc.getElementById("box1");
await showTooltip(tooltip, box1, { position: "top" });
// Get the original position of the panel and arrow.
const originalPanelBounds =
tooltip.panel.getBoxQuads({ relativeTo: doc })[0].getBounds();
const originalArrowBounds =
tooltip.arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
// Resize the content
div.style.cssText = "width: 200px; height: 30px";
tooltip.updateContainerBounds(box1, { position: "top" });
// The panel should have moved 100px to the left and 10px down
const updatedPanelBounds =
tooltip.panel.getBoxQuads({ relativeTo: doc })[0].getBounds();
const panelXMovement = `panel left: ${originalPanelBounds.left}->` +
updatedPanelBounds.left;
ok(Math.round(updatedPanelBounds.left - originalPanelBounds.left) === -100,
`Panel should have moved 100px to the left (actual: ${panelXMovement})`);
const panelYMovement = `panel top: ${originalPanelBounds.top}->` +
updatedPanelBounds.top;
ok(Math.round(updatedPanelBounds.top - originalPanelBounds.top) === 10,
`Panel should have moved 10px down (actual: ${panelYMovement})`);
// The arrow should be in the same position
const updatedArrowBounds =
tooltip.arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
const arrowXMovement = `arrow left: ${originalArrowBounds.left}->` +
updatedArrowBounds.left;
ok(Math.round(updatedArrowBounds.left - originalArrowBounds.left) === 0,
`Arrow should not have moved (actual: ${arrowXMovement})`);
const arrowYMovement = `arrow top: ${originalArrowBounds.top}->` +
updatedArrowBounds.top;
ok(Math.round(updatedArrowBounds.top - originalArrowBounds.top) === 0,
`Arrow should not have moved (actual: ${arrowYMovement})`);
await hideTooltip(tooltip);
tooltip.destroy();
});

View File

@ -422,7 +422,7 @@ HTMLTooltip.prototype = {
* @param {Element} anchor
* The reference element with which the tooltip should be aligned
* @param {Object} options
* Settings for positioning the tooltip.
* Optional settings for positioning the tooltip.
* @param {String} options.position
* Optional, possible values: top|bottom
* If layout permits, the tooltip will be displayed on top/bottom
@ -433,7 +433,55 @@ HTMLTooltip.prototype = {
* @param {Number} options.y
* Optional, vertical offset between the anchor and the tooltip.
*/
async show(anchor, {position, x = 0, y = 0} = {}) {
async show(anchor, options) {
const { left, top } = this._updateContainerBounds(anchor, options);
if (this.useXulWrapper) {
await this._showXulWrapperAt(left, top);
} else {
this.container.style.left = left + "px";
this.container.style.top = top + "px";
}
this.container.classList.add("tooltip-visible");
// Keep a pointer on the focused element to refocus it when hiding the tooltip.
this._focusedElement = this.doc.activeElement;
this.doc.defaultView.clearTimeout(this.attachEventsTimer);
this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
if (this.autofocus) {
this.focus();
}
// Update the top window reference each time in case the host changes.
this.topWindow = this._getTopWindow();
this.topWindow.addEventListener("click", this._onClick, true);
this.emit("shown");
}, 0);
},
/**
* Recalculate the dimensions and position of the tooltip in response to
* changes to its content.
*
* Parameters are identical to show().
*/
updateContainerBounds(anchor, options) {
if (!this.isVisible()) {
return;
}
const { left, top } = this._updateContainerBounds(anchor, options);
if (this.useXulWrapper) {
this._moveXulWrapperTo(left, top);
} else {
this.container.style.left = left + "px";
this.container.style.top = top + "px";
}
},
_updateContainerBounds(anchor, {position, x = 0, y = 0} = {}) {
// Get anchor geometry
let anchorRect = getRelativeRect(anchor, this.doc);
if (this.useXulWrapper) {
@ -544,28 +592,7 @@ HTMLTooltip.prototype = {
this.container.style.height = height + "px";
if (this.useXulWrapper) {
await this._showXulWrapperAt(left, top);
} else {
this.container.style.left = left + "px";
this.container.style.top = top + "px";
}
this.container.classList.add("tooltip-visible");
// Keep a pointer on the focused element to refocus it when hiding the tooltip.
this._focusedElement = this.doc.activeElement;
this.doc.defaultView.clearTimeout(this.attachEventsTimer);
this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
if (this.autofocus) {
this.focus();
}
// Update the top window reference each time in case the host changes.
this.topWindow = this._getTopWindow();
this.topWindow.addEventListener("click", this._onClick, true);
this.emit("shown");
}, 0);
return { left, top };
},
/**
@ -850,6 +877,11 @@ HTMLTooltip.prototype = {
return onPanelShown;
},
_moveXulWrapperTo: function(left, top) {
const zoom = getCurrentZoom(this.xulPanelWrapper);
this.xulPanelWrapper.moveTo(left * zoom, top * zoom);
},
_hideXulWrapper: function() {
this.xulPanelWrapper.removeEventListener("popuphidden", this._onXulPanelHidden);