UNPKG

lazy-widgets

Version:

Typescript retained mode GUI for the HTML canvas API

163 lines 6.67 kB
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 { watchArrayField } from '../decorators/FlagFields.js'; import { PointerEvent } from '../events/PointerEvent.js'; /** * The base implementation of the {@link Viewport} interface. See * {@link CanvasViewport} and {@link ClippedViewport}. * * @category Core */ export class BaseViewport { constructor(child, relativeCoordinates) { this.parent = null; /** * Should the layout be resolved, even if the child widget doesn't have a * dirty layout? */ this.forceRelayout = true; this.child = child; this.relativeCoordinates = relativeCoordinates; this.constraints = [0, Infinity, 0, Infinity]; this.rect = [0, 0, 0, 0]; this.offset = [0, 0]; } /** * Forces re-layout and calls {@link BaseViewport#updateChildPos}. Used as a * callback for the {@link BaseViewport#rect} field watcher. */ relayoutAndReposition() { this.forceRelayout = true; this.updateChildPos(); } /** * Resolves the position of the child and finalizes its bounds. This * effectively updates the position of the child in an out-of-order fashion * (doesn't wait for the proper stage of the layout resolution). Used as a * callback for the {@link BaseViewport#offset} field watcher. */ updateChildPos() { if (!this.relativeCoordinates && this.child.attached) { const [l, t, _w, _h] = this.rect; const [ox, oy] = this.offset; const newX = l + ox; const newY = t + oy; const [oldX, oldY] = this.child.position; if (newX !== oldX || newY !== oldY) { this.child.resolvePosition(newX, newY); this.child.finalizeBounds(); } } } /** * Resolves the given child's layout by calling * {@link Widget#resolveDimensions} with the current * {@link Viewport#constraints}, {@link Widget#resolvePosition} and * {@link Widget#finalizeBounds}. * * Handles both relative and absolute coordinates. The previous position is * used. * * @returns Returns true if the child was resized, else, false. */ resolveLayout() { if (!(this.child.layoutDirty || this.forceRelayout)) { return false; } this.forceRelayout = false; // Resolve child's layout const [oldWidth, oldHeight] = this.child.dimensions; this.child.resolveDimensions(...this.constraints); if (this.relativeCoordinates) { this.child.resolvePosition(0, 0); } else { this.child.resolvePosition(...this.child.idealPosition); } this.child.finalizeBounds(); const [newWidth, newHeight] = this.child.dimensions; return newWidth !== oldWidth || newHeight !== oldHeight; } dispatchTricklingEvent(event) { // Drop event if it is a positional event with no target outside the // child's viewport if (event instanceof PointerEvent) { const [cl, ct, cw, ch] = this.rect; const cr = cl + cw; const cb = ct + ch; if (event.target === null) { if (event.x < cl) { return null; } if (event.x >= cr) { return null; } if (event.y < ct) { return null; } if (event.y >= cb) { return null; } } // Correct position of pointer event if this viewport has relative // positions. if (this.relativeCoordinates) { const [ox, oy] = this.offset; const x = cl + ox; const y = ct + oy; if (x !== 0 || y !== 0) { event = event.correctOffset(x, y); } } } return this.child.dispatchEvent(event); } /** * Get the rect of the child alongside more extra information, * clipped/clamped to the bounds of the viewport. Usually only for internal, * but can be used externally if you know what you're doing. */ getClippedViewportRect() { // Calculate child's source and destination const [vpX, vpY, vpW, vpH] = this.rect; const [innerWidth, innerHeight] = this.child.dimensions; const [xOffset, yOffset] = this.offset; // viewport right and bottom const vpR = vpX + vpW; const vpB = vpY + vpH; // original child destination left and top const origXDst = vpX + xOffset; const origYDst = vpY + yOffset; // clipped child destination left, top, width and height const xDst = Math.min(Math.max(origXDst, vpX), vpR); const yDst = Math.min(Math.max(origYDst, vpY), vpB); const wClipped = Math.min(Math.max(origXDst + innerWidth, vpX), vpR) - xDst; const hClipped = Math.min(Math.max(origYDst + innerHeight, vpY), vpB) - yDst; return [vpX, vpY, vpW, vpH, origXDst, origYDst, xDst, yDst, wClipped, hClipped]; } } /** Has the warning for dimensionless canvases been issued? */ BaseViewport.dimensionlessWarned = false; /** Has the warning for non-power of 2 dimensions been issued? */ BaseViewport.powerOf2Warned = false; /** * The maximum retries allowed for * {@link Viewport#resolveLayout | resolving the layout}. The first attempt * is not counted. Only retries that exceed this limit are discarded; if * maxRelayout is 4, then the 5th retry will be discarded. */ BaseViewport.maxRelayout = 4; __decorate([ watchArrayField(BaseViewport.prototype.relayoutAndReposition) ], BaseViewport.prototype, "constraints", void 0); __decorate([ watchArrayField(BaseViewport.prototype.relayoutAndReposition) ], BaseViewport.prototype, "rect", void 0); __decorate([ watchArrayField(BaseViewport.prototype.updateChildPos) ], BaseViewport.prototype, "offset", void 0); //# sourceMappingURL=BaseViewport.js.map