moved to root
This commit is contained in:
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Popover = class Popover extends WI.Object
|
||||
{
|
||||
constructor(delegate)
|
||||
{
|
||||
super();
|
||||
|
||||
this.delegate = delegate;
|
||||
this._edge = null;
|
||||
this._frame = new WI.Rect;
|
||||
this._content = null;
|
||||
this._targetFrame = new WI.Rect;
|
||||
this._anchorPoint = new WI.Point;
|
||||
this._preferredEdges = null;
|
||||
this._resizeHandler = null;
|
||||
|
||||
this._contentNeedsUpdate = false;
|
||||
this._dismissing = false;
|
||||
|
||||
this._element = document.createElement("div");
|
||||
this._element.className = "popover";
|
||||
this._element.addEventListener("transitionend", this, true);
|
||||
|
||||
this._container = this._element.appendChild(document.createElement("div"));
|
||||
this._container.className = "container";
|
||||
|
||||
this._drawBackgroundAnimationIdentifier = undefined;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get element() { return this._element; }
|
||||
|
||||
get visible()
|
||||
{
|
||||
return this._element.parentNode === document.body && !this._element.classList.contains(WI.Popover.FadeOutClassName);
|
||||
}
|
||||
|
||||
get frame()
|
||||
{
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
set frame(frame)
|
||||
{
|
||||
this._element.style.left = frame.minX() + "px";
|
||||
this._element.style.top = frame.minY() + "px";
|
||||
this._element.style.width = frame.size.width + "px";
|
||||
this._element.style.height = frame.size.height + "px";
|
||||
this._element.style.backgroundSize = frame.size.width + "px " + frame.size.height + "px";
|
||||
this._frame = frame;
|
||||
}
|
||||
|
||||
set content(content)
|
||||
{
|
||||
if (content === this._content)
|
||||
return;
|
||||
|
||||
this._content = content;
|
||||
|
||||
this._contentNeedsUpdate = true;
|
||||
|
||||
if (this.visible)
|
||||
this._update(true);
|
||||
}
|
||||
|
||||
set windowResizeHandler(resizeHandler)
|
||||
{
|
||||
console.assert(typeof resizeHandler === "function");
|
||||
this._resizeHandler = resizeHandler;
|
||||
}
|
||||
|
||||
resize()
|
||||
{
|
||||
if (this.visible && this._resizeHandler)
|
||||
this._resizeHandler();
|
||||
}
|
||||
|
||||
update(shouldAnimate = true)
|
||||
{
|
||||
if (!this.visible)
|
||||
return;
|
||||
|
||||
var previouslyFocusedElement = document.activeElement;
|
||||
|
||||
this._contentNeedsUpdate = true;
|
||||
this._update(shouldAnimate);
|
||||
|
||||
if (previouslyFocusedElement)
|
||||
previouslyFocusedElement.focus();
|
||||
}
|
||||
|
||||
present(targetFrame, preferredEdges, {updateContent, shouldAnimate} = {})
|
||||
{
|
||||
this._targetFrame = targetFrame;
|
||||
this._preferredEdges = preferredEdges;
|
||||
|
||||
if (!this._content)
|
||||
return;
|
||||
|
||||
this._addListenersIfNeeded();
|
||||
|
||||
if (updateContent && this.visible)
|
||||
this.update(shouldAnimate);
|
||||
else
|
||||
this._update(shouldAnimate);
|
||||
}
|
||||
|
||||
presentNewContentWithFrame(content, targetFrame, preferredEdges)
|
||||
{
|
||||
this._content = content;
|
||||
this._contentNeedsUpdate = true;
|
||||
|
||||
this._targetFrame = targetFrame;
|
||||
this._preferredEdges = preferredEdges;
|
||||
|
||||
this._addListenersIfNeeded();
|
||||
|
||||
var shouldAnimate = this.visible;
|
||||
this._update(shouldAnimate);
|
||||
}
|
||||
|
||||
dismiss()
|
||||
{
|
||||
if (this._dismissing || this._element.parentNode !== document.body)
|
||||
return;
|
||||
|
||||
this._dismissing = true;
|
||||
|
||||
console.assert(this._isListeningForPopoverEvents);
|
||||
this._isListeningForPopoverEvents = false;
|
||||
|
||||
window.removeEventListener("mousedown", this, true);
|
||||
window.removeEventListener("scroll", this, true);
|
||||
window.removeEventListener("resize", this, true);
|
||||
window.removeEventListener("keypress", this, true);
|
||||
|
||||
this._prefersDarkColorSchemeMediaQueryList.removeListener(this._boundUpdate);
|
||||
|
||||
WI.quickConsole.keyboardShortcutDisabled = false;
|
||||
|
||||
this._element.classList.add(WI.Popover.FadeOutClassName);
|
||||
|
||||
if (this.delegate && typeof this.delegate.willDismissPopover === "function")
|
||||
this.delegate.willDismissPopover(this);
|
||||
}
|
||||
|
||||
handleEvent(event)
|
||||
{
|
||||
switch (event.type) {
|
||||
case "mousedown":
|
||||
case "scroll":
|
||||
if (!this._element.contains(event.target) && !event.target.closest("." + WI.Popover.IgnoreAutoDismissClassName)
|
||||
&& !event[WI.Popover.EventPreventDismissSymbol]) {
|
||||
this.dismiss();
|
||||
}
|
||||
break;
|
||||
case "resize":
|
||||
this.resize();
|
||||
break;
|
||||
case "keypress":
|
||||
if (event.keyCode === WI.KeyboardShortcut.Key.Escape.keyCode)
|
||||
this.dismiss();
|
||||
break;
|
||||
case "transitionend":
|
||||
if (event.target === this._element) {
|
||||
document.body.removeChild(this._element);
|
||||
this._element.classList.remove(WI.Popover.FadeOutClassName);
|
||||
this._container.textContent = "";
|
||||
if (this.delegate && typeof this.delegate.didDismissPopover === "function")
|
||||
this.delegate.didDismissPopover(this);
|
||||
|
||||
this._dismissing = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_update(shouldAnimate)
|
||||
{
|
||||
if (shouldAnimate)
|
||||
var previousEdge = this._edge;
|
||||
|
||||
var targetFrame = this._targetFrame;
|
||||
var preferredEdges = this._preferredEdges;
|
||||
|
||||
// Ensure our element is on display so that its metrics can be resolved
|
||||
// or interrupt any pending transition to remove it from display.
|
||||
if (this._element.parentNode !== document.body)
|
||||
document.body.appendChild(this._element);
|
||||
else
|
||||
this._element.classList.remove(WI.Popover.FadeOutClassName);
|
||||
|
||||
this._dismissing = false;
|
||||
|
||||
if (this._edge !== null)
|
||||
this._element.classList.remove(this._cssClassNameForEdge());
|
||||
|
||||
if (this._contentNeedsUpdate) {
|
||||
// Reset CSS properties on element so that the element may be sized to fit its content.
|
||||
this._element.style.removeProperty("left");
|
||||
this._element.style.removeProperty("top");
|
||||
this._element.style.removeProperty("width");
|
||||
this._element.style.removeProperty("height");
|
||||
|
||||
// Add the content in place of the wrapper to get the raw metrics.
|
||||
this._container.replaceWith(this._content);
|
||||
|
||||
// Get the ideal size for the popover to fit its content.
|
||||
var popoverBounds = this._element.getBoundingClientRect();
|
||||
this._preferredSize = new WI.Size(Math.ceil(popoverBounds.width), Math.ceil(popoverBounds.height));
|
||||
}
|
||||
|
||||
var titleBarOffset = WI.undockedTitleAreaHeight();
|
||||
var containerFrame = new WI.Rect(0, titleBarOffset, window.innerWidth, window.innerHeight - titleBarOffset);
|
||||
// The frame of the window with a little inset to make sure we have room for shadows.
|
||||
containerFrame = containerFrame.inset(WI.Popover.ShadowEdgeInsets);
|
||||
|
||||
// Work out the metrics for all edges.
|
||||
var metrics = new Array(preferredEdges.length);
|
||||
for (var edgeName in WI.RectEdge) {
|
||||
var edge = WI.RectEdge[edgeName];
|
||||
var item = {
|
||||
edge,
|
||||
metrics: this._bestMetricsForEdge(this._preferredSize, targetFrame, containerFrame, edge)
|
||||
};
|
||||
var preferredIndex = preferredEdges.indexOf(edge);
|
||||
if (preferredIndex !== -1)
|
||||
metrics[preferredIndex] = item;
|
||||
else
|
||||
metrics.push(item);
|
||||
}
|
||||
|
||||
function area(size)
|
||||
{
|
||||
return Math.max(0, size.width) * Math.max(0, size.height);
|
||||
}
|
||||
|
||||
// Find if any of those fit better than the frame for the preferred edge.
|
||||
var bestEdge = metrics[0].edge;
|
||||
var bestMetrics = metrics[0].metrics;
|
||||
for (var i = 1; i < metrics.length; i++) {
|
||||
var itemMetrics = metrics[i].metrics;
|
||||
if (area(itemMetrics.contentSize) > area(bestMetrics.contentSize)) {
|
||||
bestEdge = metrics[i].edge;
|
||||
bestMetrics = itemMetrics;
|
||||
}
|
||||
}
|
||||
|
||||
console.assert(area(bestMetrics.contentSize) > 0);
|
||||
|
||||
var anchorPoint;
|
||||
var bestFrame = bestMetrics.frame.round();
|
||||
|
||||
this._edge = bestEdge;
|
||||
|
||||
if (bestFrame === WI.Rect.ZERO_RECT) {
|
||||
// The target for the popover is offscreen.
|
||||
this.dismiss();
|
||||
} else {
|
||||
switch (bestEdge) {
|
||||
case WI.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
|
||||
anchorPoint = new WI.Point(bestFrame.size.width - WI.Popover.ShadowPadding, targetFrame.midY() - bestFrame.minY());
|
||||
break;
|
||||
case WI.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
|
||||
anchorPoint = new WI.Point(WI.Popover.ShadowPadding, targetFrame.midY() - bestFrame.minY());
|
||||
break;
|
||||
case WI.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
|
||||
anchorPoint = new WI.Point(targetFrame.midX() - bestFrame.minX(), bestFrame.size.height - WI.Popover.ShadowPadding);
|
||||
break;
|
||||
case WI.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
|
||||
anchorPoint = new WI.Point(targetFrame.midX() - bestFrame.minX(), WI.Popover.ShadowPadding);
|
||||
break;
|
||||
}
|
||||
|
||||
this._element.classList.add(this._cssClassNameForEdge());
|
||||
|
||||
if (shouldAnimate && this._edge === previousEdge)
|
||||
this._animateFrame(bestFrame, anchorPoint);
|
||||
else {
|
||||
this.frame = bestFrame;
|
||||
this._setAnchorPoint(anchorPoint);
|
||||
this._drawBackground();
|
||||
}
|
||||
|
||||
// Make sure content is centered in case either of the dimension is smaller than the minimal bounds.
|
||||
if (this._preferredSize.width < WI.Popover.MinWidth || this._preferredSize.height < WI.Popover.MinHeight)
|
||||
this._container.classList.add("center");
|
||||
else
|
||||
this._container.classList.remove("center");
|
||||
}
|
||||
|
||||
// Wrap the content in the container so that it's located correctly.
|
||||
if (this._contentNeedsUpdate) {
|
||||
this._container.textContent = "";
|
||||
this._content.replaceWith(this._container);
|
||||
this._container.appendChild(this._content);
|
||||
}
|
||||
|
||||
this._contentNeedsUpdate = false;
|
||||
}
|
||||
|
||||
_cssClassNameForEdge()
|
||||
{
|
||||
switch (this._edge) {
|
||||
case WI.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
|
||||
return "arrow-right";
|
||||
case WI.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
|
||||
return "arrow-left";
|
||||
case WI.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
|
||||
return "arrow-down";
|
||||
case WI.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
|
||||
return "arrow-up";
|
||||
}
|
||||
console.error("Unknown edge.");
|
||||
return "arrow-up";
|
||||
}
|
||||
|
||||
_setAnchorPoint(anchorPoint)
|
||||
{
|
||||
anchorPoint.x = Math.floor(anchorPoint.x);
|
||||
anchorPoint.y = Math.floor(anchorPoint.y);
|
||||
this._anchorPoint = anchorPoint;
|
||||
}
|
||||
|
||||
_animateFrame(toFrame, toAnchor)
|
||||
{
|
||||
var startTime = Date.now();
|
||||
var duration = 350;
|
||||
var epsilon = 1 / (200 * duration);
|
||||
var spline = new WI.CubicBezier(0.25, 0.1, 0.25, 1);
|
||||
|
||||
var fromFrame = this._frame.copy();
|
||||
var fromAnchor = this._anchorPoint.copy();
|
||||
|
||||
function animatedValue(from, to, progress)
|
||||
{
|
||||
return from + (to - from) * progress;
|
||||
}
|
||||
|
||||
function drawBackground()
|
||||
{
|
||||
var progress = spline.solve(Math.min((Date.now() - startTime) / duration, 1), epsilon);
|
||||
|
||||
this.frame = new WI.Rect(
|
||||
animatedValue(fromFrame.minX(), toFrame.minX(), progress),
|
||||
animatedValue(fromFrame.minY(), toFrame.minY(), progress),
|
||||
animatedValue(fromFrame.size.width, toFrame.size.width, progress),
|
||||
animatedValue(fromFrame.size.height, toFrame.size.height, progress)
|
||||
).round();
|
||||
|
||||
this._setAnchorPoint(new WI.Point(
|
||||
animatedValue(fromAnchor.x, toAnchor.x, progress),
|
||||
animatedValue(fromAnchor.y, toAnchor.y, progress)
|
||||
));
|
||||
|
||||
this._drawBackground();
|
||||
|
||||
if (progress < 1)
|
||||
this._drawBackgroundAnimationIdentifier = requestAnimationFrame(drawBackground.bind(this));
|
||||
}
|
||||
|
||||
drawBackground.call(this);
|
||||
}
|
||||
|
||||
_drawBackground()
|
||||
{
|
||||
if (this._drawBackgroundAnimationIdentifier) {
|
||||
cancelAnimationFrame(this._drawBackgroundAnimationIdentifier);
|
||||
this._drawBackgroundAnimationIdentifier = undefined;
|
||||
}
|
||||
|
||||
let scaleFactor = window.devicePixelRatio;
|
||||
|
||||
let width = this._frame.size.width;
|
||||
let height = this._frame.size.height;
|
||||
let scaledWidth = width * scaleFactor;
|
||||
let scaledHeight = height * scaleFactor;
|
||||
|
||||
// Bounds of the path don't take into account the arrow, but really only the tight bounding box
|
||||
// of the content contained within the frame.
|
||||
let bounds;
|
||||
switch (this._edge) {
|
||||
case WI.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
|
||||
bounds = new WI.Rect(0, 0, width - WI.Popover.AnchorSize, height);
|
||||
break;
|
||||
case WI.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
|
||||
bounds = new WI.Rect(WI.Popover.AnchorSize, 0, width - WI.Popover.AnchorSize, height);
|
||||
break;
|
||||
case WI.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
|
||||
bounds = new WI.Rect(0, 0, width, height - WI.Popover.AnchorSize);
|
||||
break;
|
||||
case WI.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
|
||||
bounds = new WI.Rect(0, WI.Popover.AnchorSize, width, height - WI.Popover.AnchorSize);
|
||||
break;
|
||||
}
|
||||
|
||||
bounds = bounds.inset(WI.Popover.ShadowEdgeInsets);
|
||||
let computedStyle = window.getComputedStyle(this._element, null);
|
||||
|
||||
let context = document.getCSSCanvasContext("2d", "popover", scaledWidth, scaledHeight);
|
||||
context.clearRect(0, 0, scaledWidth, scaledHeight);
|
||||
|
||||
function isolate(callback) {
|
||||
context.save();
|
||||
callback();
|
||||
context.restore();
|
||||
}
|
||||
|
||||
isolate(() => {
|
||||
context.scale(scaleFactor, scaleFactor);
|
||||
this._drawFrame(context, bounds, this._edge, this._anchorPoint);
|
||||
|
||||
isolate(() => {
|
||||
context.shadowBlur = 4;
|
||||
context.shadowColor = computedStyle.getPropertyValue("--popover-shadow-color").trim();
|
||||
|
||||
context.strokeStyle = computedStyle.getPropertyValue("--popover-border-color").trim();
|
||||
context.lineWidth = 2;
|
||||
context.stroke();
|
||||
});
|
||||
|
||||
isolate(() => {
|
||||
context.fillStyle = computedStyle.getPropertyValue("--popover-background-color").trim();
|
||||
context.fill();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_bestMetricsForEdge(preferredSize, targetFrame, containerFrame, edge)
|
||||
{
|
||||
var x, y;
|
||||
var width = preferredSize.width + (WI.Popover.ShadowPadding * 2) + (WI.Popover.ContentPadding * 2);
|
||||
var height = preferredSize.height + (WI.Popover.ShadowPadding * 2) + (WI.Popover.ContentPadding * 2);
|
||||
|
||||
switch (edge) {
|
||||
case WI.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
|
||||
width += WI.Popover.AnchorSize;
|
||||
x = targetFrame.origin.x - width + WI.Popover.ShadowPadding;
|
||||
y = targetFrame.origin.y - (height - targetFrame.size.height) / 2;
|
||||
break;
|
||||
case WI.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
|
||||
width += WI.Popover.AnchorSize;
|
||||
x = targetFrame.origin.x + targetFrame.size.width - WI.Popover.ShadowPadding;
|
||||
y = targetFrame.origin.y - (height - targetFrame.size.height) / 2;
|
||||
break;
|
||||
case WI.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
|
||||
height += WI.Popover.AnchorSize;
|
||||
x = targetFrame.origin.x - (width - targetFrame.size.width) / 2;
|
||||
y = targetFrame.origin.y - height + WI.Popover.ShadowPadding;
|
||||
break;
|
||||
case WI.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
|
||||
height += WI.Popover.AnchorSize;
|
||||
x = targetFrame.origin.x - (width - targetFrame.size.width) / 2;
|
||||
y = targetFrame.origin.y + targetFrame.size.height - WI.Popover.ShadowPadding;
|
||||
break;
|
||||
}
|
||||
|
||||
if (edge !== WI.RectEdge.MIN_X && x < containerFrame.minX())
|
||||
x = containerFrame.minX();
|
||||
if (edge !== WI.RectEdge.MAX_X && x + width > containerFrame.maxX())
|
||||
x = containerFrame.maxX() - width;
|
||||
if (edge !== WI.RectEdge.MIN_Y && y < containerFrame.minY())
|
||||
y = containerFrame.minY();
|
||||
if (edge !== WI.RectEdge.MAX_Y && y + height > containerFrame.maxY())
|
||||
y = containerFrame.maxY() - height;
|
||||
|
||||
var preferredFrame = new WI.Rect(x, y, width, height);
|
||||
var bestFrame = preferredFrame.intersectionWithRect(containerFrame);
|
||||
|
||||
width = bestFrame.size.width - (WI.Popover.ShadowPadding * 2);
|
||||
height = bestFrame.size.height - (WI.Popover.ShadowPadding * 2);
|
||||
|
||||
switch (edge) {
|
||||
case WI.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
|
||||
case WI.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
|
||||
width -= WI.Popover.AnchorSize;
|
||||
break;
|
||||
case WI.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
|
||||
case WI.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
|
||||
height -= WI.Popover.AnchorSize;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
frame: bestFrame,
|
||||
contentSize: new WI.Size(width, height)
|
||||
};
|
||||
}
|
||||
|
||||
_drawFrame(ctx, bounds, anchorEdge)
|
||||
{
|
||||
let cornerRadius = WI.Popover.CornerRadius;
|
||||
let anchorPoint = this._anchorPoint;
|
||||
|
||||
// Prevent the arrow from being positioned against one of the popover's rounded corners.
|
||||
let arrowPadding = cornerRadius + WI.Popover.AnchorSize;
|
||||
if (anchorEdge === WI.RectEdge.MIN_Y || anchorEdge === WI.RectEdge.MAX_Y)
|
||||
anchorPoint.x = Number.constrain(anchorPoint.x, bounds.minX() + arrowPadding, bounds.maxX() - arrowPadding);
|
||||
else
|
||||
anchorPoint.y = Number.constrain(anchorPoint.y, bounds.minY() + arrowPadding, bounds.maxY() - arrowPadding);
|
||||
|
||||
ctx.beginPath();
|
||||
switch (anchorEdge) {
|
||||
case WI.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
|
||||
ctx.moveTo(bounds.maxX(), bounds.minY() + cornerRadius);
|
||||
ctx.lineTo(bounds.maxX(), anchorPoint.y - WI.Popover.AnchorSize);
|
||||
ctx.lineTo(anchorPoint.x, anchorPoint.y);
|
||||
ctx.lineTo(bounds.maxX(), anchorPoint.y + WI.Popover.AnchorSize);
|
||||
ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), cornerRadius);
|
||||
ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), cornerRadius);
|
||||
ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), cornerRadius);
|
||||
ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), cornerRadius);
|
||||
break;
|
||||
case WI.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
|
||||
ctx.moveTo(bounds.minX(), bounds.maxY() - cornerRadius);
|
||||
ctx.lineTo(bounds.minX(), anchorPoint.y + WI.Popover.AnchorSize);
|
||||
ctx.lineTo(anchorPoint.x, anchorPoint.y);
|
||||
ctx.lineTo(bounds.minX(), anchorPoint.y - WI.Popover.AnchorSize);
|
||||
ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), cornerRadius);
|
||||
ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), cornerRadius);
|
||||
ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), cornerRadius);
|
||||
ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), cornerRadius);
|
||||
break;
|
||||
case WI.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
|
||||
ctx.moveTo(bounds.maxX() - cornerRadius, bounds.maxY());
|
||||
ctx.lineTo(anchorPoint.x + WI.Popover.AnchorSize, bounds.maxY());
|
||||
ctx.lineTo(anchorPoint.x, anchorPoint.y);
|
||||
ctx.lineTo(anchorPoint.x - WI.Popover.AnchorSize, bounds.maxY());
|
||||
ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), cornerRadius);
|
||||
ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), cornerRadius);
|
||||
ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), cornerRadius);
|
||||
ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), cornerRadius);
|
||||
break;
|
||||
case WI.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
|
||||
ctx.moveTo(bounds.minX() + cornerRadius, bounds.minY());
|
||||
ctx.lineTo(anchorPoint.x - WI.Popover.AnchorSize, bounds.minY());
|
||||
ctx.lineTo(anchorPoint.x, anchorPoint.y);
|
||||
ctx.lineTo(anchorPoint.x + WI.Popover.AnchorSize, bounds.minY());
|
||||
ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), cornerRadius);
|
||||
ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), cornerRadius);
|
||||
ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), cornerRadius);
|
||||
ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), cornerRadius);
|
||||
break;
|
||||
}
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
_addListenersIfNeeded()
|
||||
{
|
||||
if (!this._isListeningForPopoverEvents) {
|
||||
this._isListeningForPopoverEvents = true;
|
||||
|
||||
window.addEventListener("mousedown", this, true);
|
||||
window.addEventListener("scroll", this, true);
|
||||
window.addEventListener("resize", this, true);
|
||||
window.addEventListener("keypress", this, true);
|
||||
|
||||
if (!this._boundUpdate)
|
||||
this._boundUpdate = this._update.bind(this);
|
||||
|
||||
if (!this._prefersDarkColorSchemeMediaQueryList)
|
||||
this._prefersDarkColorSchemeMediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this._prefersDarkColorSchemeMediaQueryList.addListener(this._boundUpdate);
|
||||
|
||||
WI.quickConsole.keyboardShortcutDisabled = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WI.Popover.FadeOutClassName = "fade-out";
|
||||
WI.Popover.CornerRadius = 5;
|
||||
WI.Popover.MinWidth = 40;
|
||||
WI.Popover.MinHeight = 40;
|
||||
WI.Popover.ShadowPadding = 5;
|
||||
WI.Popover.ContentPadding = 5;
|
||||
WI.Popover.AnchorSize = 11;
|
||||
WI.Popover.ShadowEdgeInsets = new WI.EdgeInsets(WI.Popover.ShadowPadding);
|
||||
WI.Popover.IgnoreAutoDismissClassName = "popover-ignore-auto-dismiss";
|
||||
WI.Popover.EventPreventDismissSymbol = Symbol("popover-event-prevent-dismiss");
|
||||
Reference in New Issue
Block a user