lazy-widgets
Version:
Typescript retained mode GUI for the HTML canvas API
156 lines • 4.68 kB
JavaScript
import { PropagationModel } from '../events/WidgetEvent.js';
import { Parent } from './Parent.js';
/**
* A {@link Parent} widget, similar to {@link PassthroughWidget}, which can
* optionally have a single child, or no child at all, and can transfer the
* child to other WidgetSlots.
*
* @category Widget
*/
export class WidgetSlot extends Parent {
/**
* @param child - The optional single child of this widget. Can be changed later via either {@link WidgetSlot#child}, {@link WidgetSlot#transferChildTo} or {@link WidgetSlot#swapChildWith}.
*/
constructor(child = null, properties) {
super(properties);
this._child = null;
this.child = child;
}
/** The current child in this slot. May be null if the slot is empty. */
get child() {
return this._child;
}
set child(child) {
if (this._child === child) {
return;
}
if (child && child.attached) {
throw new Error('New child is already attached to another widget');
}
const isAttached = this.attached;
if (this._child && isAttached) {
this._child.detach();
}
this._child = child;
if (child) {
child.inheritedTheme = this.inheritedTheme;
if (isAttached) {
child.attach(this.root, this.viewport, this);
}
}
this._layoutDirty = true;
this.markWholeAsDirty();
}
/**
* Transfer this widget's child to a new WidgetSlot. Note that, if this
* widget has no child, then the new slot will have its child removed.
*
* Does nothing if the new slot is this.
*/
transferChildTo(newSlot) {
if (newSlot === this) {
return;
}
const child = this._child;
this.child = null;
newSlot.child = child;
}
/**
* Swap this widget's child with another WidgetSlot's child. Also works if
* either (or both) slots have no child.
*
* Does nothing if the other slot is this.
*/
swapChildWith(otherSlot) {
if (otherSlot === this) {
return;
}
const thisChild = this._child;
const otherChild = otherSlot.child;
this.child = null;
otherSlot.child = thisChild;
this.child = otherChild;
}
handleEvent(event) {
if (event.propagation !== PropagationModel.Trickling) {
return super.handleEvent(event);
}
else if (this._child) {
return this._child.dispatchEvent(event);
}
else {
return null;
}
}
handlePreLayoutUpdate() {
if (!this._child) {
return;
}
// Pre-layout update child
this._child.preLayoutUpdate();
// If child's layout is dirty, set self's layout as dirty
if (this._child && this._child.layoutDirty) {
this._layoutDirty = true;
}
}
handlePostLayoutUpdate() {
// Post-layout update child
if (this._child) {
this._child.postLayoutUpdate();
}
}
handleResolveDimensions(minWidth, maxWidth, minHeight, maxHeight) {
// Resolve child's dimensions and set own resolved dimensions to be
// equal to the child's
if (this._child) {
this._child.resolveDimensions(minWidth, maxWidth, minHeight, maxHeight);
[this.idealWidth, this.idealHeight] = this._child.idealDimensions;
}
else {
this.idealWidth = 0;
this.idealHeight = 0;
}
}
resolvePosition(x, y) {
super.resolvePosition(x, y);
// Resolve child's position to be the same as this widget's position
if (this._child) {
this._child.resolvePosition(x, y);
}
}
handlePainting(dirtyRects) {
// Paint child
if (this._child) {
this._child.paint(dirtyRects);
}
}
[Symbol.iterator]() {
const child = this._child;
let first = !!child;
return {
next() {
if (first) {
first = false;
return { value: child, done: false };
}
else {
return { value: undefined, done: true };
}
}
};
}
get childCount() {
return this._child ? 1 : 0;
}
}
WidgetSlot.autoXML = {
name: 'widget-slot',
inputConfig: [
{
mode: 'widget',
name: 'child',
optional: true,
}
]
};
//# sourceMappingURL=WidgetSlot.js.map