lazy-widgets
Version:
Typescript retained mode GUI for the HTML canvas API
197 lines • 7.98 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Alignment } from '../theme/Alignment.js';
import { SingleParent } from './SingleParent.js';
import { resolveContainerDimensions } from '../helpers/resolveContainerDimensions.js';
import { resolveContainerPosition } from '../helpers/resolveContainerPosition.js';
import { safeRoundRect } from '../helpers/safeRoundRect.js';
import { accessorAlias, layoutArrayField, layoutField } from '../decorators/FlagFields.js';
import { SingleParentXMLInputConfig } from '../xml/SingleParentXMLInputConfig.js';
import { TooltipAxisBias } from '../core/TooltipAxisBias.js';
import { PropagationModel } from '../events/WidgetEvent.js';
const startAlignment = {
horizontal: Alignment.Start, vertical: Alignment.Start
};
/**
* A container widget for a widget that will be shown when a {@link Tooltip} is
* hovered.
*
* @category Widget
*/
export class TooltipContainer extends SingleParent {
constructor(child, properties) {
var _a, _b;
super(child, properties);
/**
* The horizontal component of the position to place this tooltip around.
* Automatically set by {@link Tooltip} widget. Used for positioning.
*/
this.anchorX = 0;
/**
* The vertical component of the position to place this tooltip around.
* Automatically set by {@link Tooltip} widget. Used for positioning.
*/
this.anchorY = 0;
/**
* A rectangle representing the dimensions and position of the
* {@link Tooltip} that binds this box. Used for positioning.
*/
this.tooltipRect = [0, 0, 0, 0];
this.idealTooltipWidth = 0;
this.idealTooltipHeight = 0;
this.horizontalBias = (_a = properties === null || properties === void 0 ? void 0 : properties.horizontalBias) !== null && _a !== void 0 ? _a : TooltipAxisBias.After;
this.verticalBias = (_b = properties === null || properties === void 0 ? void 0 : properties.verticalBias) !== null && _b !== void 0 ? _b : TooltipAxisBias.Auto;
}
onThemeUpdated(property = null) {
super.onThemeUpdated(property);
if (property === null || property === 'tooltipPadding') {
this._layoutDirty = true;
this.markWholeAsDirty();
}
else if (property === 'tooltipFill' || property === 'tooltipRadii') {
this.markWholeAsDirty();
}
}
handleEvent(event) {
if (event.propagation === PropagationModel.Trickling) {
return this.child.dispatchEvent(event);
}
else {
return super.handleEvent(event);
}
}
handlePreLayoutUpdate() {
// Pre-layout update child
const child = this.child;
child.preLayoutUpdate();
// If child's layout is dirty, set self's layout as dirty
if (child.layoutDirty) {
this._layoutDirty = true;
}
}
handlePostLayoutUpdate() {
// Post-layout update child
this.child.postLayoutUpdate();
}
handleResolveDimensions(_minWidth, maxWidth, _minHeight, maxHeight) {
[this.idealTooltipWidth, this.idealTooltipHeight] = resolveContainerDimensions(0, maxWidth, 0, maxHeight, this.tooltipPadding, startAlignment, this.child);
this.idealWidth = Math.min(maxWidth, this.idealTooltipWidth);
this.idealHeight = Math.min(maxHeight, this.idealTooltipHeight);
}
resolvePosition(x, y) {
// resolve best alignment that places the child under (or over) the
// cursor
// check if there is space above or below the wrapper widget
let pX = 0;
let pY = 0;
let pW = Infinity;
let pH = Infinity;
const [wX, wY, wW, wH] = this.tooltipRect;
if (this._parent) {
[pX, pY, pW, pH] = this._parent.idealRect;
}
const spaceAbove = Math.max(wY - pY, 0);
const pBot = pY + pH;
const wBot = wY + wH;
const spaceBelow = Math.max(pBot - wBot, 0);
// decide whether to place tooltip above or below widget. fall back to
// above the widget
const fitsAbove = spaceAbove >= this.idealTooltipHeight;
const fitsBelow = spaceBelow >= this.idealTooltipHeight;
if (fitsAbove && fitsBelow) {
const vBias = this.verticalBias;
// XXX it doesn't make sense to have a centre-biased vertical axis,
// so assume the user meant to use automatic bias
if (vBias === TooltipAxisBias.Before || ((vBias === TooltipAxisBias.Center || vBias === TooltipAxisBias.Auto) && this.anchorY <= (wY + 0.5 * wH))) {
// put above
y = wY - this.idealTooltipHeight;
}
else {
// put below
y = wBot;
}
}
else if (fitsAbove) {
// put above
y = wY - this.idealTooltipHeight;
}
else if (fitsBelow) {
// put below
y = wBot;
}
else {
// put on cursor
y = this.anchorY;
}
switch (this.horizontalBias) {
case TooltipAxisBias.Before:
x = this.anchorX - this.idealTooltipWidth;
break;
case TooltipAxisBias.Center:
x = this.anchorX - this.idealTooltipWidth * 0.5;
break;
case TooltipAxisBias.After:
x = this.anchorX;
break;
default:
x = this.anchorX - ((this.anchorX <= (wX + 0.5 * wW)) ? 0 : this.idealTooltipWidth);
}
// clamp to bounds of parent
const pRight = pX + pW;
if (x + this.idealTooltipWidth > pRight) {
x = pRight - this.idealTooltipWidth;
}
if (y + this.idealTooltipHeight > pBot) {
y = pBot - this.idealTooltipHeight;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
super.resolvePosition(x, y);
resolveContainerPosition(x, y, this.idealWidth, this.idealHeight, this.tooltipPadding, startAlignment, this.child);
}
handlePainting(dirtyRects) {
const ctx = this.viewport.context;
ctx.save();
ctx.beginPath();
safeRoundRect(ctx, this.idealX, this.idealY, this.idealTooltipWidth, this.idealTooltipHeight, this.tooltipRadii);
ctx.clip();
ctx.fillStyle = this.tooltipFill;
ctx.fillRect(this.idealX, this.idealY, this.idealTooltipWidth, this.idealTooltipHeight);
this.child.paint(dirtyRects);
ctx.restore();
}
}
TooltipContainer.autoXML = {
name: 'tooltip-container',
inputConfig: SingleParentXMLInputConfig
};
__decorate([
layoutField
], TooltipContainer.prototype, "anchorX", void 0);
__decorate([
layoutField
], TooltipContainer.prototype, "anchorY", void 0);
__decorate([
accessorAlias('anchorX')
], TooltipContainer.prototype, "cursorX", void 0);
__decorate([
accessorAlias('anchorY')
], TooltipContainer.prototype, "cursorY", void 0);
__decorate([
layoutField
], TooltipContainer.prototype, "horizontalBias", void 0);
__decorate([
layoutField
], TooltipContainer.prototype, "verticalBias", void 0);
__decorate([
layoutArrayField(false)
], TooltipContainer.prototype, "tooltipRect", void 0);
//# sourceMappingURL=TooltipContainer.js.map