1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * @param {!Document} doc
9 WebInspector
.Tooltip = function(doc
)
11 this.element
= doc
.body
.createChild("div");
12 this._shadowRoot
= WebInspector
.createShadowRootWithCoreStyles(this.element
);
13 this._shadowRoot
.appendChild(WebInspector
.Widget
.createStyleElement("ui/tooltip.css"));
15 this._tooltipElement
= this._shadowRoot
.createChild("div", "tooltip");
16 doc
.addEventListener("mousemove", this._mouseMove
.bind(this), true);
17 doc
.addEventListener("mousedown", this._hide
.bind(this, true), true);
18 doc
.addEventListener("mouseout", this._hide
.bind(this, true), true);
19 doc
.addEventListener("keydown", this._hide
.bind(this, true), true);
22 WebInspector
.Tooltip
.Timing
= {
23 // Max time between tooltips showing that no opening delay is required.
24 "InstantThreshold": 300,
25 // Wait time before opening a tooltip.
29 WebInspector
.Tooltip
.prototype = {
31 * @param {!Event} event
33 _mouseMove: function(event
)
35 var path
= event
.deepPath
? event
.deepPath
: event
.path
;
36 if (!path
|| event
.buttons
!== 0)
39 if (this._anchorElement
&& path
.indexOf(this._anchorElement
) === -1)
42 for (var element
of path
) {
43 if (element
=== this._anchorElement
) {
45 } else if (element
[WebInspector
.Tooltip
._symbol
]) {
46 this._show(element
, event
);
53 * @param {!Element} anchorElement
54 * @param {!Event} event
56 _show: function(anchorElement
, event
)
58 var tooltip
= anchorElement
[WebInspector
.Tooltip
._symbol
];
59 this._anchorElement
= anchorElement
;
60 this._tooltipElement
.removeChildren();
62 // Check if native tooltips should be used.
63 for (var element
of WebInspector
.Tooltip
._nativeOverrideContainer
) {
64 if (this._anchorElement
.isSelfOrDescendant(element
)) {
65 Object
.defineProperty(this._anchorElement
, "title", WebInspector
.Tooltip
._nativeTitle
);
66 this._anchorElement
.title
= tooltip
.content
;
71 if (typeof tooltip
.content
=== "string")
72 this._tooltipElement
.textContent
= tooltip
.content
;
74 this._tooltipElement
.appendChild(tooltip
.content
);
76 if (tooltip
.actionId
) {
77 var shortcuts
= WebInspector
.shortcutRegistry
.shortcutDescriptorsForAction(tooltip
.actionId
);
78 for (var shortcut
of shortcuts
) {
79 var shortcutElement
= this._tooltipElement
.createChild("div", "tooltip-shortcut");
80 shortcutElement
.textContent
= shortcut
.name
;
84 this._tooltipElement
.classList
.add("shown");
85 // Reposition to ensure text doesn't overflow unnecessarily.
86 this._tooltipElement
.positionAt(0, 0);
88 // Show tooltip instantly if a tooltip was shown recently.
90 var instant
= (this._tooltipLastClosed
&& now
- this._tooltipLastClosed
< WebInspector
.Tooltip
.Timing
.InstantThreshold
);
91 this._tooltipElement
.classList
.toggle("instant", instant
);
92 this._tooltipLastOpened
= instant
? now
: now
+ WebInspector
.Tooltip
.Timing
.OpeningDelay
;
94 // Get container element.
95 var container
= WebInspector
.Dialog
.modalHostView().element
;
96 if (!anchorElement
.isDescendant(container
))
97 container
= this.element
.parentElement
;
99 // Posititon tooltip based on the anchor element.
100 var containerOffset
= container
.offsetRelativeToWindow(this.element
.window());
101 var containerOffsetWidth
= container
.offsetWidth
;
102 var containerOffsetHeight
= container
.offsetHeight
;
103 var anchorBox
= this._anchorElement
.boxInWindow(this.element
.window());
104 const anchorOffset
= 2;
105 const pageMargin
= 2;
106 var cursorOffset
= 10;
107 this._tooltipElement
.style
.maxWidth
= (containerOffsetWidth
- pageMargin
* 2) + "px";
108 var tooltipWidth
= this._tooltipElement
.offsetWidth
;
109 var tooltipHeight
= this._tooltipElement
.offsetHeight
;
110 var anchorTooltipAtElement
= this._anchorElement
.nodeName
=== "BUTTON" || this._anchorElement
.nodeName
=== "LABEL";
111 var tooltipX
= anchorTooltipAtElement
? anchorBox
.x
: event
.x
+ cursorOffset
;
112 tooltipX
= Number
.constrain(tooltipX
,
113 containerOffset
.x
+ pageMargin
,
114 containerOffset
.x
+ containerOffsetWidth
- tooltipWidth
- pageMargin
);
116 if (!anchorTooltipAtElement
) {
117 tooltipY
= event
.y
+ cursorOffset
+ tooltipHeight
< containerOffset
.y
+ containerOffsetHeight
? event
.y
+ cursorOffset
: event
.y
- tooltipHeight
;
119 var onBottom
= anchorBox
.y
+ anchorOffset
+ anchorBox
.height
+ tooltipHeight
< containerOffset
.y
+ containerOffsetHeight
;
120 tooltipY
= onBottom
? anchorBox
.y
+ anchorBox
.height
+ anchorOffset
: anchorBox
.y
- tooltipHeight
- anchorOffset
;
122 this._tooltipElement
.positionAt(tooltipX
, tooltipY
);
126 * @param {boolean=} removeInstant
128 _hide: function(removeInstant
)
130 delete this._anchorElement
;
131 this._tooltipElement
.classList
.remove("shown");
132 if (Date
.now() > this._tooltipLastOpened
)
133 this._tooltipLastClosed
= Date
.now();
135 delete this._tooltipLastClosed
;
139 WebInspector
.Tooltip
._symbol
= Symbol("Tooltip");
142 * @param {!Document} doc
144 WebInspector
.Tooltip
.installHandler = function(doc
)
146 new WebInspector
.Tooltip(doc
);
150 * @param {!Element} element
151 * @param {!Element|string} tooltipContent
152 * @param {string=} actionId
153 * @param {!Object=} options
155 WebInspector
.Tooltip
.install = function(element
, tooltipContent
, actionId
, options
)
157 if (typeof tooltipContent
=== "string" && tooltipContent
=== "") {
158 delete element
[WebInspector
.Tooltip
._symbol
];
161 element
[WebInspector
.Tooltip
._symbol
] = { content
: tooltipContent
, actionId
: actionId
, options
: options
|| {} };
165 * @param {!Element} element
167 WebInspector
.Tooltip
.addNativeOverrideContainer = function(element
)
169 WebInspector
.Tooltip
._nativeOverrideContainer
.push(element
);
172 /** @type {!Array.<!Element>} */
173 WebInspector
.Tooltip
._nativeOverrideContainer
= [];
174 WebInspector
.Tooltip
._nativeTitle
= /** @type {!ObjectPropertyDescriptor} */(Object
.getOwnPropertyDescriptor(HTMLElement
.prototype, "title"));
176 Object
.defineProperty(HTMLElement
.prototype, "title", {
178 * @return {!Element|string}
183 var tooltip
= this[WebInspector
.Tooltip
._symbol
];
184 return tooltip
? tooltip
.content
: "";
188 * @param {!Element|string} x
193 WebInspector
.Tooltip
.install(this, x
);