UNPKG

@aptrn/parameters-ts

Version:

Typescript library to manage MaxMsp UI

477 lines (474 loc) 20.2 kB
'use strict'; /** * Class to interact with patcher parameters UI. */ var ParametersUI = /** @class */ (function () { /** * Constructor of the UI infrastructure. Checks if infrastructure exists, creates it if not found or broken. * Once created recalls parameters with the params argument object. * @param localpatcher The patcher containing the UI elements * @param patcherID ID string used for naming the infrastructure elements * @param newValues Object of type ParamsType containing values to recall * @param instanceName If a non-empty string is provided, it will be used as a unique prefix for send object. useful to get updates via a receive object with value "instanceName_update" * @param isHeadless If true, operates in headless mode without UI infrastructure */ function ParametersUI(localpatcher, patcherID, newValues, instanceName, isHeadless) { if (instanceName === void 0) { instanceName = ""; } if (isHeadless === void 0) { isHeadless = false; } // Deep copy the values to support recursive objects this.values = JSON.parse(JSON.stringify(newValues)); this.gui = localpatcher; this.id = patcherID; this.iter = 0; this.isHeadless = isHeadless; this.isAbleton = false; var ableton = new LiveAPI(); this.isAbleton = ableton.info != ""; if (!isHeadless && this.gui == undefined) { throw new Error("Invalid patcher provided, aborting!"); } if (!isHeadless) { // Try to get existing unique prefix from infrastructure var existingPrefix = this.getExistingUniquePrefix(); // If we found an existing prefix, use it, otherwise use provided prefix or generate new one if (existingPrefix !== null) { this.uniqueId = existingPrefix + "_" + this.id; } else { this.uniqueId = this.generateRandomId(); if (instanceName !== "") { this.uniqueId += instanceName + "_"; } this.uniqueId += this.id; } this.createInfrastructure(instanceName); this.createParameters(); } else { this.uniqueId = "headless"; } } /** * Generates a random 8-character string of uppercase letters and numbers * @returns {string} Random 8-character string */ ParametersUI.prototype.generateRandomId = function () { var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var result = ''; for (var i = 0; i < 8; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result + '_'; }; /** * Constructor for headless mode - only manages values without UI infrastructure * @param newValues Object of type ParamsType containing values */ ParametersUI.headless = function (newValues, instanceName) { if (instanceName === void 0) { instanceName = ""; } return new ParametersUI(undefined, "headless", newValues, instanceName, true); }; /** * Attempts to find existing unique prefix from dict objects in the patcher * @returns {string | null} The existing prefix if found, null otherwise */ ParametersUI.prototype.getExistingUniquePrefix = function () { if (!this.gui || this.isHeadless) return null; if (this.gui.count > 0) { var obj = this.gui.firstobject; while (obj.nextobject != null) { if (obj.maxclass == "dict") { if (obj.varname == "params") { var fullName = obj.getattr("name"); if (fullName && fullName.indexOf(this.id) === fullName.length - this.id.length) { var prefix = fullName.slice(0, -this.id.length); return prefix || null; } } } obj = obj.nextobject; } } return null; }; /** * This function checks if the infrastructure exists ("id" Dict, "id_recall" Dict and "update" Send objects). * @returns {boolean} True if infrastructure exists, false otherwise */ ParametersUI.prototype.infrastructureExists = function () { if (!this.gui || this.isHeadless) return false; var hasRecall = false; var hasParams = false; var hasUpdate = false; var hasPrependUpdate = false; if (this.gui.count >= 3) { var obj = this.gui.firstobject; while (obj.nextobject != null) { var objectClass = obj.maxclass; if (objectClass == "dict") { if (obj.varname == "recall") { hasRecall = true; this.recallObj = obj; this.recallD = new Dict(String(obj.getattr("name"))); } if (obj.varname == "params") { hasParams = true; this.paramsObj = obj; this.paramsD = new Dict(String(obj.getattr("name"))); } } else if (objectClass == "s" || objectClass == "send") { if (obj.varname == "update") { hasUpdate = true; this.updateObj = obj; } } else if (objectClass == "prepend") { if (obj.varname == "prependUpdate") { hasPrependUpdate = true; } } obj = obj.nextobject; } } return hasRecall && hasParams && hasUpdate && hasPrependUpdate; }; /** * This function creates a "Dict" object named "id", a "Dict" object named "id_recall" and a Send object with target "update" * @param uniquePrefix If a non-empty string is provided, it will be used as a unique prefix for infrastructure elements * @returns {void} */ ParametersUI.prototype.createInfrastructure = function (uniquePrefix) { if (uniquePrefix === void 0) { uniquePrefix = ""; } if (!this.gui || this.isHeadless) return; if (this.infrastructureExists() == false) { this.cleanInfrastructure(); var idObj = this.gui.getnamed("id"); if (idObj) { idObj.message("set", this.uniqueId); } this.recallObj = this.gui.newdefault(50, 100, "dict", this.uniqueId + "_recall"); if (this.recallObj) { this.recallObj.varname = "recall"; } this.recallD = new Dict(this.uniqueId + "_recall"); this.paramsObj = this.gui.newdefault(200, 700, "dict", this.uniqueId); if (this.paramsObj) { this.paramsObj.varname = "params"; } this.paramsD = new Dict(this.uniqueId); var prependUpdate = this.gui.newdefault(50, 650, "prepend", this.id); if (prependUpdate) { prependUpdate.varname = "prependUpdate"; } if (uniquePrefix !== "") { this.updateObj = this.gui.newdefault(50, 700, "s", uniquePrefix + "_update"); } else { this.updateObj = this.gui.newdefault(50, 700, "s", "update"); } if (this.updateObj) { this.updateObj.varname = "update"; } // Connect prependUpdate to updateObj if (prependUpdate && this.updateObj) { this.gui.connect(prependUpdate, 0, this.updateObj, 0); } } }; ParametersUI.prototype.getUniqueId = function () { return this.uniqueId; }; /** * This function should clear "id" Dict, "id_recall" Dict and "update" Send objects. * @returns {void} */ ParametersUI.prototype.cleanInfrastructure = function () { if (!this.gui || this.isHeadless) return; if (this.gui.count > 0) { var obj = this.gui.firstobject; while (obj.nextobject != null) { var objectClass = obj.maxclass; if (objectClass == "dict") { if (obj.varname == "recall") { this.gui.remove(obj); this.recallObj = undefined; this.recallD = undefined; } if (obj.varname == "params") { this.gui.remove(obj); this.paramsObj = undefined; this.paramsD = undefined; } } else if (objectClass == "s" || objectClass == "send") { if (obj.varname == "update") { this.gui.remove(obj); this.updateObj = undefined; } } else if (objectClass == "prepend") { if (obj.varname == "prependUpdate") { this.gui.remove(obj); } } obj = obj.nextobject; } } }; /** * Creates an Object of type containtsParam<ParamsType> with all properties set to false. * @returns Object of type containtsParam<ParamsType> used to check if parameters are existing. */ ParametersUI.prototype.createContainsParam = function () { var result = Object.create(null); for (var key in this.values) { if (Object.prototype.hasOwnProperty.call(this.values, key)) { result['has_' + key] = false; } } return result; }; /** * Sets a value in the hasParams object. * @param hasParams Object of type containtsParam<ParamsType> used to check if parameters are existing. * @param paramName Property name. * @param exists Boolean value to set. */ ParametersUI.prototype.setParamExists = function (hasParams, paramName, exists) { var key = 'has_' + paramName; hasParams[key] = exists; }; /** * Gets a value in the hasParams object. * @param hasParams Object of type containtsParam<ParamsType> used to check if parameters are existing. * @param paramName Property name. * @returns Boolean value of the property. */ ParametersUI.prototype.getParamExists = function (hasParams, paramName) { var key = 'has_' + paramName; return hasParams[key]; }; /** * Returns true if all properties in the hasParams object are set to true, false otherwise. * @param hasParams Object of type containtsParam<ParamType> used to check if parameters are existing. * @returns Boolean value, true if all parameters exist, false otherwise. */ ParametersUI.prototype.allParamsExist = function (hasParams) { for (var key in hasParams) { if (Object.prototype.hasOwnProperty.call(hasParams, key)) { if (hasParams[key] === false) { return false; } } } return true; }; /** * This function checks if parameters exist in the Patcher ("pvar", "dict.unpacks" and "prepend" objects). * @returns Return true if parameters exist, false otherwise */ ParametersUI.prototype.parametersExist = function () { if (!this.gui || this.isHeadless) return false; var hasParams = this.createContainsParam(); if (this.gui.count > 0) { var obj = this.gui.firstobject; while (obj.nextobject != null) { var objectClass = obj.maxclass; if (objectClass == "pvar") { var scriptingName = obj.varname.split("_pvar")[0]; if (Object.prototype.hasOwnProperty.call(this.values, scriptingName)) { var unpack = this.gui.getnamed(scriptingName + "_unpack"); var prepend = this.gui.getnamed(scriptingName + "_prepend"); var prependSet = this.gui.getnamed(scriptingName + "_prependSet"); var tbl = this.gui.getnamed(scriptingName + "_tbl"); var actuallyHasEverything = unpack != null && prepend != null && prependSet != null && tbl != null; if (actuallyHasEverything) { this.setParamExists(hasParams, scriptingName, true); } } } obj = obj.nextobject; } } return this.allParamsExist(hasParams); }; /** * This function creates, for each property in this.params object, a "pvar" object named as the proeperty and relative parameter infrastructure as "prepend"s and "dict.unpack"s to receive from "id_recall" Dict and send to "id" Dict. * Automatically recalls and then gets parameters values using recallParams() and getParams() functions. * @param newParams Optional new parameter values * @returns {void} */ ParametersUI.prototype.createParameters = function (newParams) { if (!this.gui || this.isHeadless) return; if (this.parametersExist() == false) { this.cleanParameters(); if (newParams != undefined) { this.values = newParams; } this.iter = 0; var prependUpdate = this.gui.getnamed("prependUpdate"); for (var k in this.values) { var unpack = this.gui.newdefault(50 + 150 * this.iter, 100 + 100, "dict.unpack", k + ":"); if (unpack) { unpack.varname = k + "_unpack"; } var pvar = this.gui.newdefault(50 + 150 * this.iter, 100 + 200, "pvar", k); if (pvar) { pvar.varname = k + "_pvar"; } var prepend = this.gui.newdefault(50 + 150 * this.iter, 100 + 300, "prepend", k); if (prepend) { prepend.varname = k + "_prepend"; } var prependSet = this.gui.newdefault(50 + 150 * this.iter, 100 + 400, "prepend", "set"); if (prependSet) { prependSet.varname = k + "_prependSet"; } var tbl = this.gui.newdefault(50 + 150 * this.iter, 100 + 500, "t", k, "l"); if (tbl) { tbl.varname = k + "_tbl"; } if (this.recallObj && unpack && pvar && prepend && prependSet && tbl && prependUpdate) { this.gui.connect(this.recallObj, 0, unpack, 0); this.gui.connect(unpack, 0, pvar, 0); this.gui.connect(pvar, 0, prepend, 0); this.gui.connect(prepend, 0, prependSet, 0); this.gui.connect(prependSet, 0, tbl, 0); this.gui.connect(tbl, 1, this.paramsObj, 0); this.gui.connect(tbl, 0, prependUpdate, 0); } this.iter++; } } this.set(this.values); this.fetch(); }; /** * This function should clear all "pvar" objects and relative parameter infrastructure. * @returns {void} */ ParametersUI.prototype.cleanParameters = function () { if (!this.gui || this.isHeadless) return; if (this.gui.count > 0) { var obj = this.gui.firstobject; while (obj.nextobject != null) { var scriptingName = ""; var objectClass = obj.maxclass; if (objectClass == "pvar") { scriptingName = obj.varname.split("_pvar")[0]; if (Object.prototype.hasOwnProperty.call(this.values, scriptingName)) { var unpack = this.gui.getnamed(scriptingName + "_unpack"); var prepend = this.gui.getnamed(scriptingName + "_prepend"); var prependSet = this.gui.getnamed(scriptingName + "_prependSet"); var tbl = this.gui.getnamed(scriptingName + "_tbl"); var prependUpdate = this.gui.getnamed(scriptingName + "_prependUpdate"); if (unpack) this.gui.remove(unpack); if (prepend) this.gui.remove(prepend); if (prependSet) this.gui.remove(prependSet); if (tbl) this.gui.remove(tbl); if (prependUpdate) this.gui.remove(prependUpdate); } } obj = obj.nextobject; var pvar = this.gui.getnamed(scriptingName + "_pvar"); if (pvar) this.gui.remove(pvar); } } }; /** * This function gets parameter values * @returns {ParamsType} */ ParametersUI.prototype.fetch = function () { if (this.isHeadless) { return this.values; } if (this.paramsD != undefined) { this.values = JSON.parse(this.paramsD.stringify()); return this.values; } else { throw new Error(this.uniqueId + " Dict cannot be found, you need to create infrastructure first"); } }; /** * This function sets parameter values * @param newParams New parameter values * @returns {void} */ ParametersUI.prototype.set = function (newParams, update) { if (update === void 0) { update = true; } if (this.isHeadless) { this.values = newParams; return; } //let recallDict = this.gui?.getnamed(this.uniqueId + "_recall") as any; if (this.recallD != undefined && this.recallObj != undefined) { this.values = newParams; this.recallD.clear(); this.recallD.parse(JSON.stringify(this.values)); if (update) { this.recallObj.message("bang"); } } else { throw new Error("You need to create infrastructure first"); } }; /** * This function sets a live parameter to the dial object. * @param dial Maxobj object or Patcher that contains the parameter * @param name Parameter name * @param min Minimum value * @param max Maximum value * @param defaultValue Default value * @param enums Optional array of strings. This automatically sets parameter type to "enum" * @param isFloat Optional boolean. Sets parameter type to "float" if true, integer otherwise */ ParametersUI.prototype.setLiveParameter = function (dial, name, min, max, defaultValue, mode, // 0: float, 1: int, 2: enum enums) { if (mode === void 0) { mode = 0; } if (dial instanceof Patcher) { dial = dial.getnamed(name); } if (name != undefined) { dial.message("_parameter_shortname", name); } if (mode == 0) { dial.message("_parameter_type", 0); dial.message("_parameter_range", [min, max]); } else if (mode == 1) { dial.message("_parameter_type", 1); dial.message("_parameter_range", [min, max]); } else if (mode == 2) { dial.message("_parameter_type", 2); dial.message("_parameter_range", enums); } if (defaultValue != undefined) { dial.message("_parameter_initial", defaultValue); } }; return ParametersUI; }()); exports.ParametersUI = ParametersUI; //# sourceMappingURL=index.js.map