зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1461522 - Add doorhanger type to HTMLTooltip; r=jdescottes
MozReview-Commit-ID: 6Oq9qauwngX --HG-- extra : rebase_source : 0e6d333aa10e1580840fe107a8812260d16d0379
This commit is contained in:
Родитель
ae34b55b6a
Коммит
b8e1efcc44
|
@ -13,6 +13,8 @@ support-files =
|
|||
doc_html_tooltip.xul
|
||||
doc_html_tooltip_arrow-01.xul
|
||||
doc_html_tooltip_arrow-02.xul
|
||||
doc_html_tooltip_doorhanger-01.xul
|
||||
doc_html_tooltip_doorhanger-02.xul
|
||||
doc_html_tooltip_hover.xul
|
||||
doc_html_tooltip_rtl.xul
|
||||
doc_inplace-editor_autocomplete_offset.xul
|
||||
|
@ -138,6 +140,8 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
|||
[browser_html_tooltip_arrow-01.js]
|
||||
[browser_html_tooltip_arrow-02.js]
|
||||
[browser_html_tooltip_consecutive-show.js]
|
||||
[browser_html_tooltip_doorhanger-01.js]
|
||||
[browser_html_tooltip_doorhanger-02.js]
|
||||
[browser_html_tooltip_height-auto.js]
|
||||
[browser_html_tooltip_hover.js]
|
||||
[browser_html_tooltip_offset.js]
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/* 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 "doorhanger" type's hang direction. It should hang
|
||||
* towards the middle of the screen.
|
||||
*/
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_doorhanger-01.xul";
|
||||
|
||||
const {HTMLTooltip} =
|
||||
require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
|
||||
loadHelperScript("helper_html_tooltip.js");
|
||||
|
||||
let useXulWrapper;
|
||||
|
||||
add_task(async function() {
|
||||
// Force the toolbox to be 200px high;
|
||||
await pushPref("devtools.toolbox.footer.height", 200);
|
||||
|
||||
await addTab("about:blank");
|
||||
const [,, doc] = await createHost("bottom", TEST_URI);
|
||||
|
||||
info("Run tests for a Tooltip without using a XUL panel");
|
||||
useXulWrapper = false;
|
||||
await runTests(doc);
|
||||
|
||||
info("Run tests for a Tooltip with a XUL panel");
|
||||
useXulWrapper = true;
|
||||
await runTests(doc);
|
||||
});
|
||||
|
||||
async function runTests(doc) {
|
||||
info("Create HTML tooltip");
|
||||
const tooltip = new HTMLTooltip(doc, {type: "doorhanger", useXulWrapper});
|
||||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.width = "200px";
|
||||
div.style.height = "35px";
|
||||
tooltip.setContent(div);
|
||||
|
||||
const docBounds = doc.documentElement.getBoundingClientRect();
|
||||
|
||||
const elements = [...doc.querySelectorAll(".anchor")];
|
||||
for (const el of elements) {
|
||||
info("Display the tooltip on an anchor.");
|
||||
await showTooltip(tooltip, el);
|
||||
|
||||
const arrow = tooltip.arrow;
|
||||
ok(arrow, "Tooltip has an arrow");
|
||||
|
||||
// Get the geometry of the anchor, the tooltip panel & arrow.
|
||||
const anchorBounds = el.getBoxQuads({ relativeTo: doc })[0].getBounds();
|
||||
const panelBounds =
|
||||
tooltip.panel.getBoxQuads({ relativeTo: doc })[0].getBounds();
|
||||
const arrowBounds = arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
|
||||
|
||||
// Work out which side of the view the anchor is on.
|
||||
const center = bounds => bounds.left + bounds.width / 2;
|
||||
const anchorSide =
|
||||
center(anchorBounds) < center(docBounds)
|
||||
? "left"
|
||||
: "right";
|
||||
|
||||
// Work out which direction the doorhanger hangs.
|
||||
//
|
||||
// We can do that just by checking which edge of the panel the center of the
|
||||
// arrow is closer to.
|
||||
const panelDirection =
|
||||
center(arrowBounds) - panelBounds.left <
|
||||
panelBounds.right - center(arrowBounds)
|
||||
? "right"
|
||||
: "left";
|
||||
|
||||
const params =
|
||||
`document: ${docBounds.left}<->${docBounds.right}, ` +
|
||||
`anchor: ${anchorBounds.left}<->${anchorBounds.right}, ` +
|
||||
`panel: ${panelBounds.left}<->${panelBounds.right}, ` +
|
||||
`anchor side: ${anchorSide}, ` +
|
||||
`panel direction: ${panelDirection}`;
|
||||
ok(anchorSide !== panelDirection,
|
||||
`Doorhanger hangs towards center (${params})`);
|
||||
|
||||
await hideTooltip(tooltip);
|
||||
}
|
||||
|
||||
tooltip.destroy();
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* 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 "doorhanger" type's arrow tip is precisely centered on
|
||||
* the anchor when the anchor is small.
|
||||
*/
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_doorhanger-02.xul";
|
||||
|
||||
const {HTMLTooltip} =
|
||||
require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
|
||||
loadHelperScript("helper_html_tooltip.js");
|
||||
|
||||
let useXulWrapper;
|
||||
|
||||
add_task(async function() {
|
||||
// Force the toolbox to be 200px high;
|
||||
await pushPref("devtools.toolbox.footer.height", 200);
|
||||
|
||||
await addTab("about:blank");
|
||||
const [,, doc] = await createHost("bottom", TEST_URI);
|
||||
|
||||
info("Run tests for a Tooltip without using a XUL panel");
|
||||
useXulWrapper = false;
|
||||
await runTests(doc);
|
||||
|
||||
info("Run tests for a Tooltip with a XUL panel");
|
||||
useXulWrapper = true;
|
||||
await runTests(doc);
|
||||
});
|
||||
|
||||
async function runTests(doc) {
|
||||
info("Create HTML tooltip");
|
||||
const tooltip = new HTMLTooltip(doc, {type: "doorhanger", useXulWrapper});
|
||||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.width = "200px";
|
||||
div.style.height = "35px";
|
||||
tooltip.setContent(div);
|
||||
|
||||
const elements = [...doc.querySelectorAll(".anchor")];
|
||||
for (const el of elements) {
|
||||
info("Display the tooltip on an anchor.");
|
||||
await showTooltip(tooltip, el);
|
||||
|
||||
const arrow = tooltip.arrow;
|
||||
ok(arrow, "Tooltip has an arrow");
|
||||
|
||||
// Get the geometry of the anchor and arrow.
|
||||
const anchorBounds = el.getBoxQuads({ relativeTo: doc })[0].getBounds();
|
||||
const arrowBounds = arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
|
||||
|
||||
// Compare the centers
|
||||
const center = bounds => bounds.left + bounds.width / 2;
|
||||
const delta = Math.abs(center(anchorBounds) - center(arrowBounds));
|
||||
const describeBounds = bounds =>
|
||||
`${bounds.left}<--[${center(bounds)}]-->${bounds.right}`;
|
||||
const params =
|
||||
`anchor: ${describeBounds(anchorBounds)}, ` +
|
||||
`arrow: ${describeBounds(arrowBounds)}`;
|
||||
ok(delta < 1,
|
||||
`Arrow center is roughly aligned with anchor center (${params})`);
|
||||
|
||||
await hideTooltip(tooltip);
|
||||
}
|
||||
|
||||
tooltip.destroy();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
|
||||
<window class="theme-light"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="Tooltip test">
|
||||
|
||||
<vbox flex="1" style="position: relative">
|
||||
<!-- Left edge -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
top: 0; left: 0;">
|
||||
</html:div>
|
||||
|
||||
<!-- Not left edge but still left of center plus RTL direction (which should
|
||||
no affect the hang direction) -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
top: 0; left: 25px; direction: rtl">
|
||||
</html:div>
|
||||
|
||||
<!-- Wide but still left of center -->
|
||||
<html:div class="anchor"
|
||||
style="width:80%; height: 10px; position: absolute; background: red;
|
||||
top: 0; left: 50px;">
|
||||
</html:div>
|
||||
|
||||
<!-- Right edge -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
bottom: 0; right: 0;">
|
||||
</html:div>
|
||||
|
||||
<!-- Not right edge but still right of center plus RTL direction (which should
|
||||
no affect the hang direction) -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
bottom: 0; right: 25px; direction: rtl">
|
||||
</html:div>
|
||||
|
||||
<!-- Wide but still right of center -->
|
||||
<html:div class="anchor"
|
||||
style="width:80%; height: 10px; position: absolute; background: red;
|
||||
bottom: 0; right: 50px;">
|
||||
</html:div>
|
||||
</vbox>
|
||||
</window>
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
|
||||
<window class="theme-light"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="Tooltip test">
|
||||
|
||||
<vbox flex="1" style="position: relative">
|
||||
<!-- Towards the left -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
top: 0; left: 25px;">
|
||||
</html:div>
|
||||
|
||||
<!-- Towards the left with RTL direction -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
top: 0; left: 50px; direction: rtl;">
|
||||
</html:div>
|
||||
|
||||
<!-- Towards the right -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
bottom: 0; right: 25px;">
|
||||
</html:div>
|
||||
|
||||
<!-- Towards the right with RTL direction -->
|
||||
<html:div class="anchor"
|
||||
style="width:10px; height: 10px; position: absolute; background: red;
|
||||
bottom: 0; right: 50px; direction: rtl;">
|
||||
</html:div>
|
||||
</vbox>
|
||||
</window>
|
|
@ -24,24 +24,41 @@ module.exports.POSITION = POSITION;
|
|||
const TYPE = {
|
||||
NORMAL: "normal",
|
||||
ARROW: "arrow",
|
||||
DOORHANGER: "doorhanger",
|
||||
};
|
||||
|
||||
module.exports.TYPE = TYPE;
|
||||
|
||||
const ARROW_WIDTH = 32;
|
||||
const ARROW_WIDTH = {
|
||||
"normal": 0,
|
||||
"arrow": 32,
|
||||
// This is the value calculated for the .tooltip-arrow element in tooltip.css
|
||||
// which includes the arrow width (20px) plus the extra margin added so that
|
||||
// the drop shadow is not cropped (2px each side).
|
||||
"doorhanger": 24,
|
||||
};
|
||||
|
||||
// Default offset between the tooltip's left edge and the tooltip arrow.
|
||||
const ARROW_OFFSET = 20;
|
||||
const ARROW_OFFSET = {
|
||||
"normal": 0,
|
||||
// Default offset between the tooltip's edge and the tooltip arrow.
|
||||
"arrow": 20,
|
||||
// Match other Firefox menus which use 10px from edge (but subtract the 2px
|
||||
// margin included in the ARROW_WIDTH above).
|
||||
"doorhanger": 8,
|
||||
};
|
||||
|
||||
const EXTRA_HEIGHT = {
|
||||
"normal": 0,
|
||||
// The arrow is 16px tall, but merges on 3px with the panel border
|
||||
"arrow": 13,
|
||||
// The doorhanger arrow is 10px tall, but merges on 1px with the panel border
|
||||
"doorhanger": 9,
|
||||
};
|
||||
|
||||
const EXTRA_BORDER = {
|
||||
"normal": 0,
|
||||
"arrow": 3,
|
||||
"doorhanger": 0,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -120,12 +137,20 @@ const calculateVerticalPosition = (
|
|||
* Bounding rectangle for the viewport. top/left can be different from
|
||||
* 0 if some space should not be used by tooltips (for instance OS
|
||||
* toolbars, taskbars etc.).
|
||||
* @param {DOMRect} windowRect
|
||||
* Bounding rectangle for the window. Used to determine which direction
|
||||
* doorhangers should hang.
|
||||
* @param {Number} width
|
||||
* Preferred width for the tooltip.
|
||||
* @param {String} type
|
||||
* The tooltip type (e.g. "arrow").
|
||||
* @param {Number} offset
|
||||
* Horizontal offset in pixels.
|
||||
* @param {Number} borderRadius
|
||||
* The border radius of the panel. This is added to ARROW_OFFSET to
|
||||
* calculate the distance from the edge of the tooltip to the start
|
||||
* of arrow. It is separate from ARROW_OFFSET since it will vary by
|
||||
* platform.
|
||||
* @param {Boolean} isRtl
|
||||
* If the anchor is in RTL, the tooltip should be aligned to the right.
|
||||
* @return {Object}
|
||||
|
@ -136,13 +161,38 @@ const calculateVerticalPosition = (
|
|||
const calculateHorizontalPosition = (
|
||||
anchorRect,
|
||||
viewportRect,
|
||||
windowRect,
|
||||
width,
|
||||
type,
|
||||
offset,
|
||||
borderRadius,
|
||||
isRtl
|
||||
) => {
|
||||
// Which direction should the tooltip go?
|
||||
const hangDirection = isRtl ? "left" : "right";
|
||||
//
|
||||
// For tooltips we follow the writing direction but for doorhangers the
|
||||
// guidelines[1] say that,
|
||||
//
|
||||
// "Doorhangers opening on the right side of the view show the directional
|
||||
// arrow on the right.
|
||||
//
|
||||
// Doorhangers opening on the left side of the view show the directional
|
||||
// arrow on the left.
|
||||
//
|
||||
// Never place the directional arrow at the center of doorhangers."
|
||||
//
|
||||
// [1] https://design.firefox.com/photon/components/doorhangers.html#directional-arrow
|
||||
//
|
||||
// So for those we need to check if the anchor is more right or left.
|
||||
let hangDirection;
|
||||
if (type === TYPE.DOORHANGER) {
|
||||
const anchorCenter = anchorRect.left + anchorRect.width / 2;
|
||||
const viewCenter = windowRect.left + windowRect.width / 2;
|
||||
hangDirection = anchorCenter >= viewCenter ? "left" : "right";
|
||||
} else {
|
||||
hangDirection = isRtl ? "left" : "right";
|
||||
}
|
||||
|
||||
const anchorWidth = anchorRect.width;
|
||||
|
||||
// Calculate logical start of anchor relative to the viewport.
|
||||
|
@ -160,12 +210,14 @@ const calculateHorizontalPosition = (
|
|||
tooltipStart = Math.max(0, tooltipStart);
|
||||
|
||||
// Calculate arrow start (tooltip's start might be updated)
|
||||
const arrowWidth = type === TYPE.ARROW ? ARROW_WIDTH : 0;
|
||||
const arrowWidth = ARROW_WIDTH[type];
|
||||
let arrowStart;
|
||||
// Arrow style tooltips may need to be shifted
|
||||
if (type === TYPE.ARROW) {
|
||||
// Arrow and doorhanger style tooltips may need to be shifted
|
||||
if (type === TYPE.ARROW || type === TYPE.DOORHANGER) {
|
||||
const arrowOffset = ARROW_OFFSET[type] + borderRadius;
|
||||
|
||||
// Where will the point of the arrow be if we apply the standard offset?
|
||||
const arrowCenter = tooltipStart + ARROW_OFFSET + arrowWidth / 2;
|
||||
const arrowCenter = tooltipStart + arrowOffset + arrowWidth / 2;
|
||||
|
||||
// How does that compare to the center of the anchor?
|
||||
const anchorCenter = anchorStart + anchorWidth / 2;
|
||||
|
@ -175,12 +227,12 @@ const calculateHorizontalPosition = (
|
|||
tooltipStart = Math.max(0, tooltipStart - (arrowCenter - anchorCenter));
|
||||
}
|
||||
// Arrow's start offset relative to the anchor.
|
||||
arrowStart = Math.min(ARROW_OFFSET, (anchorWidth - arrowWidth) / 2) | 0;
|
||||
arrowStart = Math.min(arrowOffset, (anchorWidth - arrowWidth) / 2) | 0;
|
||||
// Translate the coordinate to tooltip container
|
||||
arrowStart += anchorStart - tooltipStart;
|
||||
// Make sure the arrow remains in the tooltip container.
|
||||
arrowStart = Math.min(arrowStart, tooltipWidth - arrowWidth);
|
||||
arrowStart = Math.max(arrowStart, 0);
|
||||
arrowStart = Math.min(arrowStart, tooltipWidth - arrowWidth - borderRadius);
|
||||
arrowStart = Math.max(arrowStart, borderRadius);
|
||||
}
|
||||
|
||||
// Convert from logical coordinates to physical
|
||||
|
@ -232,7 +284,8 @@ const getRelativeRect = function(node, relativeTo) {
|
|||
* The toolbox document to attach the HTMLTooltip popup.
|
||||
* @param {Object}
|
||||
* - {String} type
|
||||
* Display type of the tooltip. Possible values: "normal", "arrow"
|
||||
* Display type of the tooltip. Possible values: "normal", "arrow", and
|
||||
* "doorhanger".
|
||||
* - {Boolean} autofocus
|
||||
* Defaults to false. Should the tooltip be focused when opening it.
|
||||
* - {Boolean} consumeOutsideClicks
|
||||
|
@ -375,8 +428,7 @@ HTMLTooltip.prototype = {
|
|||
anchorRect = this._convertToScreenRect(anchorRect);
|
||||
}
|
||||
|
||||
// Get viewport size
|
||||
const viewportRect = this._getViewportRect();
|
||||
const { viewportRect, windowRect } = this._getBoundingRects();
|
||||
|
||||
// Calculate the horizonal position and width
|
||||
let preferredWidth;
|
||||
|
@ -399,9 +451,29 @@ HTMLTooltip.prototype = {
|
|||
}
|
||||
|
||||
const anchorWin = anchor.ownerDocument.defaultView;
|
||||
const isRtl = anchorWin.getComputedStyle(anchor).direction === "rtl";
|
||||
const anchorCS = anchorWin.getComputedStyle(anchor);
|
||||
const isRtl = anchorCS.direction === "rtl";
|
||||
|
||||
let borderRadius = 0;
|
||||
if (this.type === TYPE.DOORHANGER) {
|
||||
borderRadius = parseFloat(
|
||||
anchorCS.getPropertyValue("--theme-arrowpanel-border-radius")
|
||||
);
|
||||
if (Number.isNaN(borderRadius)) {
|
||||
borderRadius = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const {left, width, arrowLeft} = calculateHorizontalPosition(
|
||||
anchorRect, viewportRect, preferredWidth, this.type, x, isRtl);
|
||||
anchorRect,
|
||||
viewportRect,
|
||||
windowRect,
|
||||
preferredWidth,
|
||||
this.type,
|
||||
x,
|
||||
borderRadius,
|
||||
isRtl
|
||||
);
|
||||
|
||||
// If we constrained the width, then any measured height we have is no
|
||||
// longer valid.
|
||||
|
@ -411,10 +483,22 @@ HTMLTooltip.prototype = {
|
|||
|
||||
// Apply width and arrow positioning
|
||||
this.container.style.width = width + "px";
|
||||
if (this.type === TYPE.ARROW) {
|
||||
if (this.type === TYPE.ARROW || this.type === TYPE.DOORHANGER) {
|
||||
this.arrow.style.left = arrowLeft + "px";
|
||||
}
|
||||
|
||||
// Work out how much vertical margin we have.
|
||||
//
|
||||
// This relies on us having set either .tooltip-top or .tooltip-bottom
|
||||
// and on the margins for both being symmetrical. Fortunately the call to
|
||||
// _measureContainerSize above will set .tooltip-top for us and it also
|
||||
// assumes these styles are symmetrical so this should be ok.
|
||||
const panelWindow = this.panel.ownerDocument.defaultView;
|
||||
const panelComputedStyle = panelWindow.getComputedStyle(this.panel);
|
||||
const verticalMargin =
|
||||
parseFloat(panelComputedStyle.marginTop) +
|
||||
parseFloat(panelComputedStyle.marginBottom);
|
||||
|
||||
// Calculate the vertical position and height
|
||||
let preferredHeight;
|
||||
if (this.preferredHeight === "auto") {
|
||||
|
@ -424,8 +508,12 @@ HTMLTooltip.prototype = {
|
|||
} else {
|
||||
({ height: preferredHeight } = this._measureContainerSize());
|
||||
}
|
||||
preferredHeight += verticalMargin;
|
||||
} else {
|
||||
const themeHeight = EXTRA_HEIGHT[this.type] + 2 * EXTRA_BORDER[this.type];
|
||||
const themeHeight =
|
||||
EXTRA_HEIGHT[this.type] +
|
||||
verticalMargin +
|
||||
2 * EXTRA_BORDER[this.type];
|
||||
preferredHeight = this.preferredHeight + themeHeight;
|
||||
}
|
||||
|
||||
|
@ -467,22 +555,47 @@ HTMLTooltip.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Calculate the rect of the viewport that limits the tooltip dimensions. When using a
|
||||
* XUL panel wrapper, the viewport will be able to use the whole screen (excluding space
|
||||
* reserved by the OS for toolbars etc.). Otherwise, the viewport is limited to the
|
||||
* tooltip's document.
|
||||
* Calculate the following boundary rectangles:
|
||||
*
|
||||
* @return {Object} DOMRect-like object with the Number properties: top, right, bottom,
|
||||
* left, width, height
|
||||
* - Viewport rect: This is the region that limits the tooltip dimensions.
|
||||
* When using a XUL panel wrapper, the tooltip will be able to use the whole
|
||||
* screen (excluding space reserved by the OS for toolbars etc.) and hence
|
||||
* the result will be in screen coordinates.
|
||||
* Otherwise, the tooltip is limited to the tooltip's document.
|
||||
*
|
||||
* - Window rect: This is the bounds of the view in which the tooltip is
|
||||
* presented. It is reported in the same coordinates as the viewport
|
||||
* rect and is used for determining in which direction a doorhanger-type
|
||||
* tooltip should "hang".
|
||||
* When using the XUL panel wrapper this will be the dimensions of the
|
||||
* window in screen coordinates. Otherwise it will be the same as the
|
||||
* viewport rect.
|
||||
*
|
||||
* @return {Object} An object with the following properties
|
||||
* viewportRect {Object} DOMRect-like object with the Number
|
||||
* properties: top, right, bottom, left, width, height
|
||||
* representing the viewport rect.
|
||||
* windowRect {Object} DOMRect-like object with the Number
|
||||
* properties: top, right, bottom, left, width, height
|
||||
* representing the viewport rect.
|
||||
*/
|
||||
_getViewportRect: function() {
|
||||
_getBoundingRects: function() {
|
||||
let viewportRect;
|
||||
let windowRect;
|
||||
|
||||
if (this.useXulWrapper) {
|
||||
// availLeft/Top are the coordinates first pixel available on the screen for
|
||||
// applications (excluding space dedicated for OS toolbars, menus etc...)
|
||||
// availWidth/Height are the dimensions available to applications excluding all
|
||||
// the OS reserved space
|
||||
const {availLeft, availTop, availHeight, availWidth} = this.doc.defaultView.screen;
|
||||
return {
|
||||
// availLeft/Top are the coordinates first pixel available on the screen
|
||||
// for applications (excluding space dedicated for OS toolbars, menus
|
||||
// etc...)
|
||||
// availWidth/Height are the dimensions available to applications
|
||||
// excluding all the OS reserved space
|
||||
const {
|
||||
availLeft,
|
||||
availTop,
|
||||
availHeight,
|
||||
availWidth,
|
||||
} = this.doc.defaultView.screen;
|
||||
viewportRect = {
|
||||
top: availTop,
|
||||
right: availLeft + availWidth,
|
||||
bottom: availTop + availHeight,
|
||||
|
@ -490,9 +603,27 @@ HTMLTooltip.prototype = {
|
|||
width: availWidth,
|
||||
height: availHeight,
|
||||
};
|
||||
|
||||
const {
|
||||
screenX,
|
||||
screenY,
|
||||
outerWidth,
|
||||
outerHeight,
|
||||
} = this.doc.defaultView;
|
||||
windowRect = {
|
||||
top: screenY,
|
||||
right: screenX + outerWidth,
|
||||
bottom: screenY + outerHeight,
|
||||
left: screenX,
|
||||
width: outerWidth,
|
||||
height: outerHeight,
|
||||
};
|
||||
} else {
|
||||
viewportRect = windowRect =
|
||||
this.doc.documentElement.getBoundingClientRect();
|
||||
}
|
||||
|
||||
return this.doc.documentElement.getBoundingClientRect();
|
||||
return { viewportRect, windowRect };
|
||||
},
|
||||
|
||||
_measureContainerSize: function() {
|
||||
|
@ -573,7 +704,7 @@ HTMLTooltip.prototype = {
|
|||
let html = '<div class="tooltip-filler"></div>';
|
||||
html += '<div class="tooltip-panel"></div>';
|
||||
|
||||
if (this.type === TYPE.ARROW) {
|
||||
if (this.type === TYPE.ARROW || this.type === TYPE.DOORHANGER) {
|
||||
html += '<div class="tooltip-arrow"></div>';
|
||||
}
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
|
|
|
@ -262,6 +262,212 @@
|
|||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
/* Tooltip : doorhanger style */
|
||||
|
||||
:root {
|
||||
--theme-arrowpanel-border-radius: 0px;
|
||||
}
|
||||
:root[platform="mac"] {
|
||||
--theme-arrowpanel-border-radius: 3.5px;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] > .tooltip-panel {
|
||||
padding: 4px 0;
|
||||
color: var(--theme-arrowpanel-color);
|
||||
margin: 4px;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] > .tooltip-panel,
|
||||
.tooltip-container[type="doorhanger"] > .tooltip-arrow::before {
|
||||
background: var(--theme-arrowpanel-background);
|
||||
border: 1px solid var(--theme-arrowpanel-border-color);
|
||||
border-radius: var(--theme-arrowpanel-border-radius);
|
||||
box-shadow: 0 0 4px hsla(210,4%,10%,.2);
|
||||
}
|
||||
|
||||
:root[platform="mac"] .tooltip-container[type="doorhanger"] > .tooltip-panel,
|
||||
:root[platform="mac"] .tooltip-container[type="doorhanger"] > .tooltip-arrow::before {
|
||||
box-shadow: none;
|
||||
/*
|
||||
* The above should be:
|
||||
*
|
||||
* box-shadow: 0 0 0 1px var(--theme-arrowpanel-border-color);
|
||||
*
|
||||
* but although that gives the right emphasis to the border it makes the
|
||||
* platform shadow much too dark.
|
||||
*/
|
||||
}
|
||||
|
||||
:root[platform="mac"].theme-light .tooltip-container[type="doorhanger"] > .tooltip-panel,
|
||||
:root[platform="mac"].theme-light .tooltip-container[type="doorhanger"] > .tooltip-arrow::before {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] > .tooltip-arrow {
|
||||
/* Desired width of the arrow */
|
||||
--arrow-width: 20px;
|
||||
|
||||
/* Amount of room to allow for the shadow. Should be about half the radius. */
|
||||
--shadow-radius: 4px;
|
||||
--shadow-margin: calc(var(--shadow-radius) / 2);
|
||||
|
||||
/*
|
||||
* Crop the arrow region to show half the arrow plus allow room for margins.
|
||||
*
|
||||
* The ARROW_WIDTH in HTMLTooltip.js needs to match the following value.
|
||||
*/
|
||||
width: calc(var(--arrow-width) + 2 * var(--shadow-margin));
|
||||
height: calc(var(--arrow-width) / 2 + var(--shadow-margin));
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] > .tooltip-arrow::before {
|
||||
/* Make sure the border is included in the size */
|
||||
box-sizing: border-box;
|
||||
|
||||
/* Don't inherit any rounded corners. */
|
||||
border-radius: 0;
|
||||
|
||||
/*
|
||||
* When the box is rotated, it should have width <arrow-width>.
|
||||
* That makes the length of one side of the box equal to:
|
||||
*
|
||||
* (<arrow-width> / 2) / sin 45
|
||||
*/
|
||||
--sin-45: 0.707106781;
|
||||
--square-side: calc(var(--arrow-width) / 2 / var(--sin-45));
|
||||
width: var(--square-side);
|
||||
height: var(--square-side);
|
||||
|
||||
/*
|
||||
* The rotated square will overshoot the left side
|
||||
* and need to be shifted in by:
|
||||
*
|
||||
* (<arrow-width> - square side) / 2
|
||||
*
|
||||
* But we also want to shift it in so that the box-shadow
|
||||
* is not clipped when we clip the parent so we add
|
||||
* a suitable margin for that.
|
||||
*/
|
||||
--overhang: calc((var(--arrow-width) - var(--square-side)) / 2);
|
||||
margin-left: calc(var(--overhang) + var(--shadow-margin));
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"].tooltip-top > .tooltip-panel {
|
||||
/* Drop the margin between the doorhanger and the arrow. */
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"].tooltip-bottom > .tooltip-panel {
|
||||
/* Drop the margin between the doorhanger and the arrow. */
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"].tooltip-top > .tooltip-arrow {
|
||||
/* Overlap the arrow with the 1px border of the doorhanger */
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"].tooltip-bottom > .tooltip-arrow {
|
||||
/* Overlap the arrow with the 1px border of the doorhanger */
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"].tooltip-top > .tooltip-arrow::before {
|
||||
/* Show only the bottom half of the box */
|
||||
margin-top: calc(var(--square-side) / -2);
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"].tooltip-bottom > .tooltip-arrow::before {
|
||||
/* Shift the rotated box in so that it is not clipped */
|
||||
margin-top: calc(var(--overhang) + var(--shadow-margin));
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .tooltip-panel ul {
|
||||
/* Override the display: -moz-box declaration in minimal-xul.css
|
||||
* or else menu items won't stack. */
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin: 0;
|
||||
padding: 4px 12px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > button.command:-moz-any([role="menuitem"],[role="menuitemcheckbox"]) {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
color: var(--theme-arrowpanel-color);
|
||||
background-color: transparent;
|
||||
text-align: start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command:not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus) {
|
||||
background-color: var(--theme-arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command:-moz-focusring::-moz-focus-inner {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command:not([disabled]):-moz-any([open],:hover:active) {
|
||||
background-color: var(--theme-arrowpanel-dimmed-further);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command[aria-checked="true"] {
|
||||
list-style-image: none;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
background: url(chrome://browser/skin/check.svg) no-repeat transparent;
|
||||
background-size: 11px 11px;
|
||||
background-position: center left 7px;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command[aria-checked="true"]:-moz-locale-dir(rtl) {
|
||||
background-position: center right 7px;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command > .label {
|
||||
flex: 1;
|
||||
padding-inline-start: 16px;
|
||||
font: menu;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command.iconic > .label::before {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
margin-inline-end: 8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: top;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
/*
|
||||
* The icons in the sidebar menu have opacity: 0.8 here, but those in the
|
||||
* hamburger menu don't. For now we match the hamburger menu styling,
|
||||
* especially because the 80% opacity makes the icons look dull in dark mode.
|
||||
*/
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] .menuitem > .command > .accelerator {
|
||||
margin-inline-start: 10px;
|
||||
color: var(--theme-arrowpanel-disabled-color);
|
||||
font: message-box;
|
||||
}
|
||||
|
||||
.tooltip-container[type="doorhanger"] hr {
|
||||
display: block;
|
||||
border: none;
|
||||
border-top: 1px solid var(--theme-arrowpanel-separator);
|
||||
margin: 6px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Tooltip: Events */
|
||||
|
||||
.event-header {
|
||||
|
|
|
@ -93,6 +93,17 @@
|
|||
--theme-tooltip-background: rgba(255, 255, 255, .9);
|
||||
--theme-tooltip-shadow: rgba(155, 155, 155, 0.26);
|
||||
|
||||
/* Doorhangers */
|
||||
/* These colors are based on the colors used for doorhangers elsewhere in
|
||||
* Firefox. */
|
||||
--theme-arrowpanel-background: white;
|
||||
--theme-arrowpanel-color: -moz-fieldText;
|
||||
--theme-arrowpanel-border-color: var(--grey-90-a20);
|
||||
--theme-arrowpanel-separator: var(--grey-90-a20);
|
||||
--theme-arrowpanel-dimmed: hsla(0,0%,80%,.3);
|
||||
--theme-arrowpanel-dimmed-further: hsla(0,0%,80%,.45);
|
||||
--theme-arrowpanel-disabled-color: GrayText;
|
||||
|
||||
/* Command line */
|
||||
--theme-command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme);
|
||||
--theme-command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme-focus);
|
||||
|
@ -101,6 +112,16 @@
|
|||
--theme-messageCloseButtonFilter: invert(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* For doorhangers elsewhere in Fireflox, Mac uses a fixed color different to
|
||||
* -moz-fieldText and a slightly lighter border color (presumably since it
|
||||
* combines with the platform shadow).
|
||||
*/
|
||||
:root[platform="mac"].theme-light {
|
||||
--theme-arrowpanel-color: rgb(26,26,26);
|
||||
--theme-arrowpanel-border-color: hsla(210,4%,10%,.05);
|
||||
}
|
||||
|
||||
:root.theme-dark {
|
||||
--theme-body-background: var(--grey-80);
|
||||
--theme-sidebar-background: #1B1B1D;
|
||||
|
@ -180,6 +201,17 @@
|
|||
--theme-tooltip-background: rgba(19, 28, 38, .9);
|
||||
--theme-tooltip-shadow: rgba(25, 25, 25, 0.76);
|
||||
|
||||
/* Doorhangers */
|
||||
/* These colors are based on the colors used for doorhangers elsewhere in
|
||||
* Firefox. */
|
||||
--theme-arrowpanel-background: var(--grey-60);
|
||||
--theme-arrowpanel-color: rgb(249,249,250);
|
||||
--theme-arrowpanel-border-color: #27272b;
|
||||
--theme-arrowpanel-separator: rgba(249,249,250,.1);
|
||||
--theme-arrowpanel-dimmed: rgba(249,249,250,.1);
|
||||
--theme-arrowpanel-dimmed-further: rgba(249,249,250,.15);
|
||||
--theme-arrowpanel-disabled-color: rgba(249,249,250,.5);
|
||||
|
||||
/* Command line */
|
||||
--theme-command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme);
|
||||
--theme-command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme-focus);
|
||||
|
@ -252,5 +284,6 @@
|
|||
--grey-80: #2a2a2e;
|
||||
--grey-90: #0c0c0d;
|
||||
--grey-90-a10: rgba(12, 12, 13, 0.1);
|
||||
--grey-90-a20: rgba(12, 12, 13, 0.2);
|
||||
--grey-90-a80: rgba(12, 12, 13, 0.8);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче