@aptrn/parameters-ts
Version:
Typescript library to manage MaxMsp UI
477 lines (474 loc) • 20.2 kB
JavaScript
'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