UNPKG

lazy-widgets

Version:

Typescript retained mode GUI for the HTML canvas API

121 lines 5.5 kB
import { CanvasViewport } from '../core/CanvasViewport.js'; import { Widget } from './Widget.js'; import { PropagationModel } from '../events/WidgetEvent.js'; import { SingleParentXMLInputConfig } from '../xml/SingleParentXMLInputConfig.js'; import { viewportRelativePointToAbsolute } from '../helpers/viewportRelativePointToAbsolute.js'; import { viewportRelativeRectToAbsolute } from '../helpers/viewportRelativeRectToAbsolute.js'; import { clipRelativeRectToAbsoluteViewport } from '../helpers/clipRelativeRectToAbsoluteViewport.js'; import { BaseContainer } from './BaseContainer.js'; import { resolveContainerChildConstraints } from '../helpers/resolveContainerChildConstraints.js'; /** * Similar to {@link Container}, but the child is painted to a * {@link CanvasViewport}. There is no reason to use this directly, but you can * if you have a niche reason for it. The intended use is to do effects on the * child by modifying how the canvas is painted in a base class. */ export class CanvasContainer extends BaseContainer { constructor(child, properties) { super(child, properties); this.internalViewport = new CanvasViewport(child); } handleEvent(event) { if (event.propagation === PropagationModel.Trickling) { return this.internalViewport.dispatchTricklingEvent(event); } else { // XXX this is slightly inneficient, because all BaseContainer does // is check if it's a trickling event, otherwise, it calls // super.handleEvent. we know it isn't though, but the // alternative is to call the grandparent class' handleEvent, // which is horrible design return super.handleEvent(event); } } handlePreLayoutUpdate() { super.handlePreLayoutUpdate(); // Update viewport resolution if needed this.internalViewport.resolution = this.root.resolution; } finalizeBounds() { super.finalizeBounds(); // Update viewport rect const padding = this.containerPadding; this.internalViewport.rect = [ this.x + padding.left, this.y + padding.top, Math.max(0, this.width - padding.left - padding.right), Math.max(0, this.height - padding.top - padding.bottom), ]; } handleResolveDimensions(minWidth, maxWidth, minHeight, maxHeight) { const padding = this.containerPadding; const hPadding = padding.left + padding.right; const vPadding = padding.top + padding.bottom; this.internalViewport.constraints = resolveContainerChildConstraints(minWidth, maxWidth, minHeight, maxHeight, hPadding, vPadding, this.containerAlignment); this.internalViewport.resolveLayout(); [this.idealWidth, this.idealHeight] = this.child.idealDimensions; this.idealWidth += hPadding; this.idealHeight += vPadding; } resolvePosition(x, y) { // FIXME we shouldn't have to do this, this is horrible... Widget.prototype.resolvePosition.call(this, x, y); this.child.resolvePosition(0, 0); } handleAttachment() { // XXX don't call super.handleAttachment, otherwise the child is // attached to the parent viewport instead of the internal viewport this.internalViewport.parent = this._viewport; this.child.attach(this._root, this.internalViewport, this); } handleDetachment() { // unset parent viewport of internal viewport this.internalViewport.parent = null; super.handleDetachment(); } /** * Paint the internal canvas to the parent viewport. Override this method if * you want to apply effects to the child widget. */ handleInternalCanvasPainting(clippedViewportRect) { this.internalViewport.paintToParentViewport(clippedViewportRect, false); } handlePainting(_dirtyRects) { const clippedViewportRect = this.internalViewport.getClippedViewportRect(); this.internalViewport.paintToInternal(); this.handleInternalCanvasPainting(clippedViewportRect); } /** * Transform or discard a damage region from the child widget in the canvas. * Override this method if you have non-local effects, such as shadows. * * The damage region is a mutable reference; you are expected to transform * it if necessary. * * @returns `true` if the damage region should be propagated, or `false` otherwise */ handleCanvasDamage(_rect) { return true; } propagateDirtyRect(rect) { // canvas viewports are painted independently, so we need to mark // regions in them as dirty this.internalViewport.pushDirtyRect([...rect]); const clippedRect = clipRelativeRectToAbsoluteViewport(this.internalViewport, this.rect, rect); if (!clippedRect || !this.handleCanvasDamage(clippedRect)) { return; } super.propagateDirtyRect(clippedRect); } queryRect(rect, relativeTo = null) { return super.queryRect(viewportRelativeRectToAbsolute(this.internalViewport, rect), relativeTo); } queryPoint(x, y, relativeTo = null) { return super.queryPoint(...viewportRelativePointToAbsolute(this.internalViewport, x, y), relativeTo); } } CanvasContainer.autoXML = { name: 'canvas-container', inputConfig: SingleParentXMLInputConfig }; //# sourceMappingURL=CanvasContainer.js.map