lazy-widgets
Version:
Typescript retained mode GUI for the HTML canvas API
121 lines • 5.5 kB
JavaScript
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