UNPKG

malwoden

Version:

![alt text](./coverage/badge-lines.svg) ![alt text](./coverage/badge-statements.svg) ![alt text](./coverage/badge-functions.svg) ![alt text](./coverage/badge-branches.svg)

372 lines 13.3 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Widget = void 0; var Calc = require("../calc"); /** * A Widget represents a reusable component able to draw to a terminal. These don't have to be used * if you prefer to directly write to the terminal itself, but can simplify the process. The abstract class * here contains basic shared methods related to Widgets, like adding/removing children, calculating * position, etc. * * While Malwoden provides several widgets out of the box, this class can be easily extended to make * custom widgets of all shapes and sizes. */ var Widget = /** @class */ (function () { /** * Creates a new Widget. * @param config - WidgetConfig */ function Widget(config) { var _a; this.origin = { x: 0, y: 0 }; this.children = []; this.disabled = false; this.origin = (_a = config.origin) !== null && _a !== void 0 ? _a : { x: 0, y: 0 }; this.state = config.initialState; this.absoluteOrigin = this.origin; this.updateAbsoluteOrigin(); } // --------------------------------------------------------------------------- // Primary Getters/Setters // --------------------------------------------------------------------------- /** * Sets the terminal for this widget and all children widgets. Will be passed * to any children added in the future as well. * @param terminal - Leave empty to clear * @returns this */ Widget.prototype.setTerminal = function (terminal) { this.terminal = terminal; for (var _i = 0, _a = this.children; _i < _a.length; _i++) { var c = _a[_i]; c.setTerminal(terminal); } return this; }; Widget.prototype.registerMouseContext = function (mouseContext) { this.clearMouseContext(); this.mouseRegistration = { mouseContext: mouseContext, mouseOnUp: this.cascadeMouseClick.bind(this), mouseOnDown: this.cascadeMouseClick.bind(this), }; mouseContext.onMouseUp(this.mouseRegistration.mouseOnUp); mouseContext.onMouseDown(this.mouseRegistration.mouseOnDown); return this; }; Widget.prototype.clearMouseContext = function () { if (this.mouseRegistration) { this.mouseRegistration.mouseContext.clearMouseUp(this.mouseRegistration.mouseOnUp); this.mouseRegistration.mouseContext.clearMouseDown(this.mouseRegistration.mouseOnDown); } this.mouseRegistration = undefined; return this; }; /** * Sets the mouseHandler for this widget and all children widgets. Will be passed * to any children added in the future as well. * @param mouseHandler - Leave empty to clear * @returns this */ Widget.prototype.setMouseHandler = function (mouseHandler) { this.mouseHandler = mouseHandler; for (var _i = 0, _a = this.children; _i < _a.length; _i++) { var c = _a[_i]; c.setMouseHandler(mouseHandler); } return this; }; /** * Gets the current state of the widget. While not a copy, it's recommended to * use setState rather than mutate this object. * @returns */ Widget.prototype.getState = function () { return this.state; }; /** * Sets the state of the widget. Partial values allowed. * @param state Partial<S> * @returns - The widget */ Widget.prototype.setState = function (state) { Object.assign(this.state, state); return this; }; /** * Sets the disabled state. * @param disabled - Default true * @returns - The Widget */ Widget.prototype.setDisabled = function (disabled) { if (disabled === void 0) { disabled = true; } this.disabled = disabled; return this; }; // --------------------------------------------------------------------------- // Position Related // --------------------------------------------------------------------------- /** * Sets the local origin of the widget relative to it's parent, * then updates the absoluteOrigin of the widget * @param origin Vector2 - The position relative to its parent * @returns - The widget */ Widget.prototype.setOrigin = function (origin) { this.origin = origin; this.updateAbsoluteOrigin(); return this; }; /** * Gets the local origin. This will be relative to the parent's origin. * @returns - Vector2 */ Widget.prototype.getOrigin = function () { return this.origin; }; /** * Whether or not the widget will update/draw * @returns boolean */ Widget.prototype.isDisabled = function () { return this.disabled; }; /** * Returns a Vector2 relative to true 0,0, which is usually the top left of the terminal. * @returns - Vector2 */ Widget.prototype.getAbsoluteOrigin = function () { return this.absoluteOrigin; }; /** * Transforms the given local position to an absolute position. * @param localPosition Vector2 * @returns Vector2 */ Widget.prototype.localToAbsolute = function (localPosition) { return Calc.Vector.add(this.absoluteOrigin, localPosition); }; /** * Transforms a given absolute position to one relative to the * widget's local origin. * @param absolutePosition Vector2 * @returns Vector2 */ Widget.prototype.absoluteToLocal = function (absolutePosition) { return Calc.Vector.subtract(absolutePosition, this.absoluteOrigin); }; // --------------------------------------------------------------------------- // Parent/Child Functions // --------------------------------------------------------------------------- /** * Updates the absoluteOrigin position, called * whenever a widget's local origin has moved. */ Widget.prototype.updateAbsoluteOrigin = function () { if (this.parent === undefined) { this.absoluteOrigin = __assign({}, this.origin); } else { var parentAbs = this.parent.absoluteOrigin; this.absoluteOrigin = Calc.Vector.add(parentAbs, this.origin); } for (var _i = 0, _a = this.children; _i < _a.length; _i++) { var c = _a[_i]; c.updateAbsoluteOrigin(); } }; /** * Adds the widget to a parent. * @param parent * @returns this - The child widget */ Widget.prototype.setParent = function (parent) { parent.addChild(this); return this; }; /** * Adds a child widget to this widget. Removes any existing parent from the child first. * @param child - The child widget * @returns The child widget */ Widget.prototype.addChild = function (child) { if (child.parent) { child.parent.removeChild(child); } child.parent = this; child.updateAbsoluteOrigin(); child.setTerminal(this.terminal); child.setMouseHandler(this.mouseHandler); this.children.push(child); return child; }; /** * Removes a child widget from the parent widget. Clears inherited Terminal/MouseHandler/KeyboardHandler * @param child The child widget * @returns The child widget if found, undefined otherwise. */ Widget.prototype.removeChild = function (child) { var newChildren = []; var foundChild; for (var _i = 0, _a = this.children; _i < _a.length; _i++) { var c = _a[_i]; if (c !== child) { newChildren.push(c); } else { foundChild = c; } } this.children = newChildren; if (foundChild) { foundChild.setTerminal(undefined); foundChild.setMouseHandler(undefined); } return foundChild; }; // --------------------------------------------------------------------------- // Updating // --------------------------------------------------------------------------- /** * Set a function to run whenever update or cascadeUpdate is called. Generally this is used * with closures/currying to transform game state to function state. * @param updateFunc - The function called on update * @returns The widget */ Widget.prototype.setUpdateFunc = function (updateFunc) { this.updateFunc = updateFunc; return this; }; /** * Clears the function run on update. * @returns The Widget */ Widget.prototype.clearUpdateFunc = function () { this.updateFunc = undefined; return this; }; /** * Calls update() for this widget and all children widgets recursively. * Children added last will be called last. If a widget is disabled, the update * will stop there. */ Widget.prototype.cascadeUpdate = function () { if (this.isDisabled()) { return; } if (this.updateFunc) { var s = this.updateFunc(); Object.assign(this.state, s); } for (var _i = 0, _a = this.children; _i < _a.length; _i++) { var c = _a[_i]; c.cascadeUpdate(); } }; /** * Calls an updateFunc if previously given, and merges it into the * widgets state. */ Widget.prototype.update = function () { if (this.isDisabled()) { return; } if (!this.updateFunc) return; var s = this.updateFunc(); Object.assign(this.state, s); }; // --------------------------------------------------------------------------- // Drawing // --------------------------------------------------------------------------- /** * Calls the draw() method of this widget and all children widgets recursively. * If a widget is disabled it will stop the cascade. */ Widget.prototype.cascadeDraw = function () { if (this.isDisabled()) return; this.draw(); for (var _i = 0, _a = this.children; _i < _a.length; _i++) { var c = _a[_i]; c.cascadeDraw(); } }; /** * Draws on to the given terminal using the widget's current state. Will not draw if disabled. * @returns */ Widget.prototype.draw = function () { if (this.isDisabled()) return; this.onDraw(); }; // --------------------------------------------------------------------------- // Clicking // --------------------------------------------------------------------------- /** * Sends a mouse event through a tree of Widgets. If any widget's onMouseClick * returns true, the event will be captured and stop cascading. * * Widgets are called in reverse order, so the last one to render will be * the first one called for onMouseClick. * @param mouse */ Widget.prototype.cascadeMouseClick = function (event) { if (this.isDisabled()) return false; for (var i = this.children.length - 1; i >= 0; i--) { var c = this.children[i]; var captured = c.cascadeMouseClick(event); if (captured) return true; } return this.onMouseClick(event); }; /** * Calls onMouseClick if the widget is not disabled * @param event - MouseHanderEvent */ Widget.prototype.mouseClick = function (event) { if (this.isDisabled()) return false; return this.onMouseClick(event); }; /** * Will be fired on click() or cascadeClick(). Return 'true' to capture the event, which * will make it no longer cascade to other widgets. Return 'false' to pass the event to other widgets. * @param absolutePosition Vector2 - The absolute position of the rendering terminal * @returns boolean - Capture the event */ Widget.prototype.onMouseClick = function (mouse) { return false; }; // -------------------------------------------------------------------------- // Drawing // -------------------------------------------------------------------------- /** * Draws a glyph on the terminal using a position local to the widget. * @param pos - A local position * @param glyph - The glyph to draw * @returns */ Widget.prototype.drawGlyph = function (pos, glyph) { if (!this.terminal) return; this.terminal.drawGlyph({ x: this.absoluteOrigin.x + pos.x, y: this.absoluteOrigin.y + pos.y }, glyph); }; return Widget; }()); exports.Widget = Widget; //# sourceMappingURL=widget.js.map