new timestamp attribute, few simplifications/optimization

This commit is contained in:
2026-06-30 14:15:06 +02:00
parent 26e7fd1ca4
commit 1897983305

View File

@@ -24,7 +24,7 @@ autowatch = 0;
// A "loadbang, pattrstorage test" message does the trick. // A "loadbang, pattrstorage test" message does the trick.
inlets = 1 inlets = 1
setinletassist(0, "Connect to the linked pattrstorage"); setinletassist(0, "Connect to pattrstorage");
outlets = 5; outlets = 5;
setoutletassist(0, "Outputs last triggered action"); setoutletassist(0, "Outputs last triggered action");
@@ -83,6 +83,7 @@ var send_name = "none"; // The global name to send presets dict name to (rec
var unique_names = false; // When enabled, force names to be unique when renaming slots by appending "bis" to them. Gets applied only to subsequently renamed presets. var unique_names = false; // When enabled, force names to be unique when renaming slots by appending "bis" to them. Gets applied only to subsequently renamed presets.
var autoname = false; // Automatically name new presets when created as "Preset" followed by the slot id var autoname = false; // Automatically name new presets when created as "Preset" followed by the slot id
var use_uid = 0; // Generating UID for each presets when enabled. Requires a [pattr preset_metadata] var use_uid = 0; // Generating UID for each presets when enabled. Requires a [pattr preset_metadata]
var timestamp = false; // Generates a `created` and `modified` timestamp for each preset. Requires use_uid enabled
var recall_passthrough = true; // By default (true), clicking a slot sends a recall message directly to [pattrstorage], and the jsui left outlet outputs a recall message once the recall is done. When disabled, clicking a slot will send a recall message straight from the jsui left outlet, so it's up to the user to forward the message to pattrstorage. It can be usefull for triggering interpolations with custom logic. var recall_passthrough = true; // By default (true), clicking a slot sends a recall message directly to [pattrstorage], and the jsui left outlet outputs a recall message once the recall is done. When disabled, clicking a slot will send a recall message straight from the jsui left outlet, so it's up to the user to forward the message to pattrstorage. It can be usefull for triggering interpolations with custom logic.
var ui_rename = false; // Use the attached textedit, if any, to edit slot names directly in the JSUI frame when clicking a slot while holding the control key. When disabled, the textedit remains untouched but gets focused when clicking a slot while holding the control key. var ui_rename = false; // Use the attached textedit, if any, to edit slot names directly in the JSUI frame when clicking a slot while holding the control key. When disabled, the textedit remains untouched but gets focused when clicking a slot while holding the control key.
var poll_edited = 0; // If >0, check if current preset is edited every X seconds defined by the variable value. var poll_edited = 0; // If >0, check if current preset is edited every X seconds defined by the variable value.
@@ -154,36 +155,25 @@ var poll_edited_task = new Task(do_poll_edited, this);
var has_loaded = false; var has_loaded = false;
if (jsarguments.length>1) { // Depreciated, use "pattrstorage" attribute instead of jsarguments. if (jsarguments.length>1) { // Depreciated, use "pattrstorage" attribute instead of jsarguments, or directly connect tc.preset to pattrstorage after its creation.
setpattrstorage(jsarguments[1]); setpattrstorage(jsarguments[1]);
} }
// FUNCTIONS // FUNCTIONS
function slot(left, top, right, bottom, name, lock, interp, color_index, color_custom, uid, filled) { function slot(left, top, right, bottom) {
this.left = left; this.left = left ? left : 0;
this.top = top; this.top = top ? top : 0;
this.right = right; this.right = right ? right : 0;
this.bottom = bottom; this.bottom = bottom ? bottom : 0;
this.name = name; this.name = null;
this.lock = lock; this.lock = 0;
this.interp = interp; this.interp = -1;
this.color_index = color_index; this.color_index = 0;
this.color_custom = color_custom; this.color_custom = stored_slot_color;
this.uid = uid; this.uid = 0;
this.filled = filled; this.filled = false;
this.created = null;
this.init = function() { this.modified = null;
this.left = 0;
this.top = 0;
this.right = 0;
this.bottom = 0;
this.name = null;
this.lock = 0;
this.interp = -1;
this.init_color();
this.uid = 0;
this.filled = false;
}
this.init_color = function() { this.init_color = function() {
this.color_index = 0; this.color_index = 0;
@@ -198,8 +188,33 @@ function slot(left, top, right, bottom, name, lock, interp, color_index, color_c
this.color_custom = stored_slot_color; this.color_custom = stored_slot_color;
this.uid = 0; this.uid = 0;
this.filled = false; this.filled = false;
this.created = null;
this.modified = null;
}
this.set_geom = function(left, top, right, bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
this.export_metadata = function () {
var meta_dict = {};
if (color_mode > 1) {
var cstm = this.color_custom;
var color_val = [this.color_index, cstm[0], cstm[1], cstm[2], cstm[3]];
meta_dict['color']= color_val;
}
if (use_uid) meta_dict['uid'] = this.uid;
if (timestamp) {
meta_dict['created'] = this.created;
meta_dict['modified'] = this.modified;
}
return JSON.stringify(meta_dict);
} }
} }
slot.local = 1;
function loadbang() { function loadbang() {
// post("loadbang\n"); // post("loadbang\n");
@@ -210,7 +225,6 @@ function loadbang() {
psto_auto_link_task.repeat(); psto_auto_link_task.repeat();
} }
calc_rows_columns(); calc_rows_columns();
find_textedit(); find_textedit();
} }
@@ -274,9 +288,6 @@ function calc_rows_columns() {
rows += 2; rows += 2;
} }
} }
}
if (scrollable && nbslot_edit) {
} }
true_slots_count_display = columns * rows - minus_slots_carry; true_slots_count_display = columns * rows - minus_slots_carry;
slots_count_display = scrollable && nbslot_edit ? true_slots_count_display - 2 : true_slots_count_display; slots_count_display = scrollable && nbslot_edit ? true_slots_count_display - 2 : true_slots_count_display;
@@ -289,19 +300,18 @@ function calc_rows_columns() {
var right = left + slot_size; var right = left + slot_size;
var cur = 1 + i * columns + j; var cur = 1 + i * columns + j;
var prev_state = new slot(); if (slots[cur]) {
prev_state.init(); slots[cur].set_geom(left, top, right, bottom);
if (typeof slots[cur] !== 'undefined') { } else {
prev_state = slots[cur]; slots[cur] = new slot(left, top, right, bottom);
} }
slots[cur] = new slot(left, top, right, bottom, prev_state.name, prev_state.lock, prev_state.interp, prev_state.color_index, prev_state.color_custom, prev_state.uid, prev_state.filled);
} }
} }
if (slots_count_display < slots_highest) { if (slots_count_display < slots_highest) {
for (var i = slots_count_display + 1; i <= slots_highest; i++) { for (var i = slots_count_display + 1; i <= slots_highest; i++) {
slots[i] = new slot(); slots[i] = new slot();
slots[i].init();
} }
} }
paint_base(); paint_base();
@@ -313,24 +323,24 @@ function draw_slot(id, scale, cont) {
cont = typeof cont !== 'undefined' ? cont : mgraphics; // Sets drawing context to mgraphics by default if not passed as argument cont = typeof cont !== 'undefined' ? cont : mgraphics; // Sets drawing context to mgraphics by default if not passed as argument
var offset = slot_size * (1 - scale); var offset = slot_size * (1 - scale);
var slot = slots[id];
if(is_painting_base) { if(is_painting_base) {
draw_slot_bubble(slots[id].left * scale, slots[id].top *scale, slot_size * scale, slot_size * scale, cont); draw_slot_bubble(slot.left * scale, slot.top *scale, slot_size * scale, slot_size * scale, cont);
} else { } else {
draw_slot_bubble(slots[id].left + offset, slots[id].top + offset, slot_size * scale, slot_size * scale, cont); draw_slot_bubble(slot.left + offset, slot.top + offset, slot_size * scale, slot_size * scale, cont);
} }
cont.fill(); cont.fill();
if (layout == 1) { if (layout == 1) {
// slot text background // slot text background
var bg_txt_pos_x = margin + slot_size + spacing; var bg_txt_pos_x = margin + slot_size + spacing;
var bg_txt_pos_y = slots[id].top; var bg_txt_pos_y = slot.top;
var bg_txt_dim_w = ui_width - (2*margin + slot_size + spacing); var bg_txt_dim_w = ui_width - (2*margin + slot_size + spacing);
var bg_txt_dim_h = slot_size; var bg_txt_dim_h = slot_size;
if (slots[id].filled) { if (slot.filled) {
cont.set_source_rgba(stored_slot_color); cont.set_source_rgba(stored_slot_color);
} else { } else {
cont.set_source_rgba(empty_slot_color); cont.set_source_rgba(empty_slot_color);
@@ -361,12 +371,12 @@ function draw_slot(id, scale, cont) {
cont.set_line_width(Math.floor(slot_size * 0.3)); cont.set_line_width(Math.floor(slot_size * 0.3));
if (id > true_slots_count_display - 2) { if (id > true_slots_count_display - 2) {
cont.set_source_rgba(stored_slot_color); cont.set_source_rgba(stored_slot_color);
cont.move_to((slots[id].left + slot_size * 0.1) * scale, (slots[id].top + half_slot_size) * scale); cont.move_to((slot.left + slot_size * 0.1) * scale, (slot.top + half_slot_size) * scale);
cont.rel_line_to(slot_size * 0.8 * scale, 0); cont.rel_line_to(slot_size * 0.8 * scale, 0);
cont.stroke(); cont.stroke();
} }
if (id > true_slots_count_display - 1) { if (id > true_slots_count_display - 1) {
cont.move_to((slots[id].left + half_slot_size) * scale, (slots[id].top + slot_size * 0.1) * scale); cont.move_to((slot.left + half_slot_size) * scale, (slot.top + slot_size * 0.1) * scale);
cont.rel_line_to(0, slot_size * 0.8 * scale); cont.rel_line_to(0, slot_size * 0.8 * scale);
cont.stroke(); cont.stroke();
} }
@@ -405,13 +415,14 @@ draw_text_bubble.local = 1;
function format_slot_name(id) { function format_slot_name(id) {
var text = id; var text = id;
var slot = slots[id];
// If slot is locked, add brackets around its number // If slot is locked, add brackets around its number
if (slots[id].lock == 1) { if (slot.lock == 1) {
text = '[' + text + ']'; text = '[' + text + ']';
} }
// If slot has a name, append it to the preset name // If slot has a name, append it to the preset name
if ([null, '<(unnamed)>'].indexOf(slots[id].name) < 0 ) { if ([null, '<(unnamed)>'].indexOf(slot.name) < 0 ) {
text += ': ' + slots[id].name; text += ': ' + slot.name;
} }
text = text.toString(); text = text.toString();
return text; return text;
@@ -436,13 +447,14 @@ function paint_base() {
// All slots // All slots
for (var i = 1; i <= true_slots_count_display; i++) { for (var i = 1; i <= true_slots_count_display; i++) {
if (i != drag_slot) { //We mask the slot that is currently dragged as it is drawn at the mouse position already if (i != drag_slot) { //We mask the slot that is currently dragged as it is drawn at the mouse position already
if (slots[i].filled == true) { var slot = slots[i];
if (slot.filled == true) {
if (color_mode == 1) { if (color_mode == 1) {
mg.set_source_rgba(color_wheel_custom[i % color_wheel_size]); mg.set_source_rgba(color_wheel_custom[i % color_wheel_size]);
} else if (color_mode == 2) { } else if (color_mode == 2) {
mg.set_source_rgba(color_wheel_custom[Math.abs(slots[i].color_index) % color_wheel_size]); mg.set_source_rgba(color_wheel_custom[Math.abs(slot.color_index) % color_wheel_size]);
} else if (color_mode == 3) { } else if (color_mode == 3) {
mg.set_source_rgba(slots[i].color_custom); mg.set_source_rgba(slot.color_custom);
} else { } else {
mg.set_source_rgba(stored_slot_color); mg.set_source_rgba(stored_slot_color);
} }
@@ -525,12 +537,13 @@ function paint()
if (is_dragging == 0 && display_interp && is_interpolating) { if (is_dragging == 0 && display_interp && is_interpolating) {
for (var i = 1; i <= slots_count_display; i++) { for (var i = 1; i <= slots_count_display; i++) {
var interp = slots[i].interp; var slot = slots[i];
var interp = slot.interp;
if (interp >= 0) { if (interp >= 0) {
mgraphics.set_source_rgba(interp_slot_color); mgraphics.set_source_rgba(interp_slot_color);
draw_slot_bubble(slots[i].left, slots[i].top, slot_size, slot_size); draw_slot_bubble(slot.left, slot.top, slot_size, slot_size);
mgraphics.stroke(); mgraphics.stroke();
draw_slot_bubble(slots[i].left, slots[i].top + slot_size * (1-interp), slot_size, slot_size * interp); draw_slot_bubble(slot.left, slot.top + slot_size * (1-interp), slot_size, slot_size * interp);
mgraphics.fill(); mgraphics.fill();
} }
} }
@@ -816,9 +829,7 @@ function get_metadata_pattr_address() {
get_metadata_pattr_address.local = 1; get_metadata_pattr_address.local = 1;
function update_preset_metadata_pattr(s) { function update_preset_metadata_pattr(s) {
var cstm = slots[s].color_custom; to_pattrstorage("setstoredvalue", "preset_metadata", s, slots[s].export_metadata());
var color_val = [slots[s].color_index, cstm[0], cstm[1], cstm[2], cstm[3]];
to_pattrstorage("setstoredvalue", "preset_metadata", s, JSON.stringify({color: color_val, uid: slots[s].uid}));
} }
update_preset_metadata_pattr.local = 1; update_preset_metadata_pattr.local = 1;
@@ -861,6 +872,7 @@ function get_preset_metadata(s) {
get_preset_metadata.local = 1; get_preset_metadata.local = 1;
function preset_metadata(v) { function preset_metadata(v) {
// Parsing received data from preset_metadata pattr
var meta_dict = {}; var meta_dict = {};
if (requested_slot > -1 && v) { if (requested_slot > -1 && v) {
meta_dict = JSON.parse(v); meta_dict = JSON.parse(v);
@@ -879,9 +891,13 @@ function preset_metadata(v) {
slots[requested_slot].color_custom = stored_slot_color; slots[requested_slot].color_custom = stored_slot_color;
} }
} }
if (use_uid && meta_dict.hasOwnProperty('uid') && meta_dict.uid) { if (meta_dict.hasOwnProperty('uid') && meta_dict.uid) {
slots[requested_slot].uid = meta_dict.uid; slots[requested_slot].uid = meta_dict.uid;
} }
if (meta_dict.hasOwnProperty('created')) {
slots[requested_slot].created = meta_dict.created;
slots[requested_slot].modified = meta_dict.modified;
}
} }
} }
@@ -973,11 +989,9 @@ function slotlist() {
if (slots_count_display < slots_highest) { if (slots_count_display < slots_highest) {
for (var i = slots_count_display + 1; i <= slots_highest; i++) { for (var i = slots_count_display + 1; i <= slots_highest; i++) {
slots[i] = new slot(); slots[i] = new slot();
slots[i].init();
} }
} }
for (var i = 0; i < filled_slots.length; i++) { for (var i = 0; i < filled_slots.length; i++) {
// to_pattrstorage("getslotname", filled_slots[i]);
slots[filled_slots[i]].filled = true; slots[filled_slots[i]].filled = true;
} }
is_updating_names = 1; is_updating_names = 1;
@@ -1187,13 +1201,20 @@ function store(v) {
slots[v].uid = generateUID(); slots[v].uid = generateUID();
} }
slots[v].init_color(); slots[v].init_color();
if (timestamp) {
var date = new Date().toISOString();
slots[v].created = date;
slots[v].modified = date;
}
} else {
if (timestamp) {
var date = new Date().toISOString();
slots[v].modified = date;
}
} }
// Updating metadata in pattr object before storing in pattrstorage // Updating metadata in pattr object before storing in pattrstorage
var cstm = slots[v].color_custom; metadata_pattr.message(slots[v].export_metadata());
var color_val = [slots[v].color_index, cstm[0], cstm[1], cstm[2], cstm[3]];
var meta_dict = {color: color_val, uid: slots[v].uid};
metadata_pattr.message(JSON.stringify(meta_dict));
} }
to_pattrstorage("store", v); to_pattrstorage("store", v);
@@ -1374,12 +1395,10 @@ function readfile(f, s) {
} }
function resolvealias(alias, client) { function resolvealias(alias, client) {
post(pattrstorage_name, "resolvealias", alias, client, '\n');
if (alias === waiting_for_alias) { if (alias === waiting_for_alias) {
if (waiting_for_alias === 'preset_metadata') { if (waiting_for_alias === 'preset_metadata') {
if (client) { if (client) {
// All good, alias already set // All good, alias already set
post("alias already found\n")
is_listening_to_clientlist = 0; is_listening_to_clientlist = 0;
return return
} else { } else {
@@ -1474,7 +1493,7 @@ function select(v) {
} }
function slots_clear() { function slots_clear() {
slots[0].init(); slots[0].clear();
slots[0].name = "(tmp)"; slots[0].name = "(tmp)";
for (var i = 1; i < slots.length; i++) { for (var i = 1; i < slots.length; i++) {
slots[i].clear(); slots[i].clear();
@@ -1484,13 +1503,14 @@ slots_clear.local = 1;
function get_slot_index(x, y) { function get_slot_index(x, y) {
// Returns which slot is hovered by the mouse // Returns which slot is hovered by the mouse
for (var i = 1; i <= true_slots_count_display; i++) { for (var i = 1; i <= true_slots_count_display; i++) {
var slot = slots[i];
if (layout === 0) { if (layout === 0) {
if (y > (slots[i].top - half_spacing) && y < (slots[i].bottom + half_spacing) && x > (slots[i].left - half_spacing) && x < (slots[i].right + half_spacing)) { if (y > (slot.top - half_spacing) && y < (slot.bottom + half_spacing) && x > (slot.left - half_spacing) && x < (slot.right + half_spacing)) {
return i; return i;
} }
} else if (layout === 1) { } else if (layout === 1) {
if (y > (slots[i].top - half_spacing) && y < (slots[i].bottom + half_spacing)) { if (y > (slot.top - half_spacing) && y < (slot.bottom + half_spacing)) {
return i; return i;
} }
} }
@@ -1545,18 +1565,25 @@ function update_filled_slots_dict() {
var by_uid = {}; var by_uid = {};
for (var i = 0; i < filled_slots.length; i++) { for (var i = 0; i < filled_slots.length; i++) {
var slot_index = filled_slots[i]; var slot_index = filled_slots[i];
var slot = slots[slot_index];
var tmp_dict = { var tmp_dict = {
index: slot_index, // Preset slot number (unique, non-persistent) index: slot_index, // Preset slot number (unique, non-persistent)
name: slots[slot_index].name, // Preset name (non unique, non-persistent) name: slot.name, // Preset name (non unique, non-persistent)
uid: slots[slot_index].uid, // Preset uid (unique and persistent across preset renaming, overwriting and moving) Useful for keeping track of preset across changes u_name: slot_index + ' | ' + slot.name, // '3 | Third preset': (unique, non-persistent) Useful for easy preset listing and retrieving
u_name: slot_index + ' | ' + slots[slot_index].name, // '3 | Third preset': (unique, non-persistent) Useful for easy preset listing and retrieving lock: slot.lock, // Preset lock state
lock: slots[slot_index].lock, // Preset lock state
color_index: slots[slot_index].color_index, // Preset color index (when color_mode = 2)
color_custom: slots[slot_index].color_custom, // Preset color (when color_mode = 3)
}; };
// post('updating by_uid', slots[slot_index].uid, '\n'); if (color_mode > 1) {
tmp_dict['color_index'] = slot.color_index; // Preset color index (when color_mode = 2)
tmp_dict['color_custom'] = slot.color_custom; // Preset color (when color_mode = 3)
}
if (use_uid) tmp_dict['uid'] = slot.uid; // Preset uid (unique and persistent across preset renaming, overwriting and moving) Useful for keeping track of preset across changes
// post('updating by_uid', slot.uid, '\n');
if (timestamp) {
tmp_dict['created'] = slot.created;
tmp_dict['modified'] = slot.modified;
}
by_slot[slot_index] = tmp_dict; by_slot[slot_index] = tmp_dict;
if (use_uid) by_uid[slots[slot_index].uid] = tmp_dict; if (use_uid) by_uid[slot.uid] = tmp_dict;
} }
filled_slots_dict.setparse('by_slot', JSON.stringify(by_slot)); filled_slots_dict.setparse('by_slot', JSON.stringify(by_slot));
if (use_uid) filled_slots_dict.setparse('by_uid', JSON.stringify(by_uid)); if (use_uid) filled_slots_dict.setparse('by_uid', JSON.stringify(by_uid));
@@ -1564,7 +1591,6 @@ function update_filled_slots_dict() {
var tmp_send_name = send_name == "none" ? pattrstorage_name + '_presets_dict' : send_name; var tmp_send_name = send_name == "none" ? pattrstorage_name + '_presets_dict' : send_name;
outlet(4, 'dictionary', filled_slots_dict.name); outlet(4, 'dictionary', filled_slots_dict.name);
messnamed(tmp_send_name, 'dictionary', filled_slots_dict.name); messnamed(tmp_send_name, 'dictionary', filled_slots_dict.name);
} }
update_filled_slots_dict.local = 1; update_filled_slots_dict.local = 1;
@@ -2465,6 +2491,17 @@ function setuse_uid(v){
} }
setuse_uid.local = 1; setuse_uid.local = 1;
declareattribute("timestamp", null, "settimestamp", 1, {style: "onoff", label: "Generate timestamp metadata"});
function settimestamp(v){
var new_val = v === 1;
if (new_val && !use_uid) {
error("timestamp requires use_uid to be enabled");
new_val = false;
}
timestamp = new_val;
}
settimestamp.local = 1;
declareattribute("recall_passthrough", "getrecall_passthrough", "setrecall_passthrough", 1, {style: "onoff", label: "Recall Passthrough"}); declareattribute("recall_passthrough", "getrecall_passthrough", "setrecall_passthrough", 1, {style: "onoff", label: "Recall Passthrough"});
function getrecall_passthrough() { function getrecall_passthrough() {
return recall_passthrough; return recall_passthrough;