Compare commits

...

2 Commits

2 changed files with 183 additions and 12 deletions

View File

@@ -167,6 +167,10 @@
<description>When set to 1, the jsui will display the ongoing interpolation between presets when a recall message with three arguments or a recallmulti message with at least one argument are sent to the linked pattrstorage.
Notice that the recallmutli message needs to be sent both the pattrstorage and the jsui.</description>
</attribute>
<attribute name='edited_color' get='1' set='1' type='list' size='4' >
<digest>Edited Dot Color</digest>
<description>Defines the color of the dot shown on top of a slot when the corresponding preset is edited. See poll_edited attribute.</description>
</attribute>
<attribute name='empty_slot_color' get='1' set='1' type='list' size='4' >
<digest>Empty slot color</digest>
<description>Sets the empty slot color of the object in RGBA format</description>
@@ -194,10 +198,14 @@
<description>Set the name of the [pattrstorage] to bind to. Its outlet must be connected to the [tc.preset] inlet.
</description>
</attribute>
<attribute name='recall_passthrough' get='1' set='1' type='bool' size='1' >
<attribute name='recall_passthrough' get='1' set='1' type='bool' size='1' >
<digest>Send recall messages to pattrstorage directly</digest>
<description>When enabled (default) recall messages triggered by clicking filled slots in tc.preset are sent directly to [pattrstorage]. When disabled, these recall messages are sent out of [tc.preset] left outlet and it's up to the user to pass them to [pattrstorage]. It can be usefull for triggering interpolations with custom logic.</description>
</attribute>
<attribute name='poll_edited' get='1' set='1' type='bool' size='1' >
<digest>Regularly check if current preset is edited</digest>
<description>When set to a positive value, [tc.preset] will check if the last recalled preset has been edited by polling [pattrstorage] with the 'getedited' message every period of time defined by the value. For example, a value of 0.5 will result in a polling every half second. When a preset is edited, a dot appears on top of its slot.</description>
</attribute>
<attribute name='scrollable' get='1' set='1' type='bool' size='1' >
<digest>Scroll through your presets</digest>
<description>When set to 1, you can through the jsui to see all your presets, or at least up to the row defined by the min_rows attributes.

View File

@@ -56,6 +56,7 @@ var stored_slot_color = [0.502, 0.502, 0.502, 1];
var interp_slot_color = [1.0, 1.0, 1.0, 0.8];
var text_bg_color = [1,1,1, 0.5];
var text_color = [0.129, 0.129, 0.129, 1];
var edited_color = [1, 0.49, 0.263, 1];
var color_1 = [0.743, 0.41, 0.501, 1]; // Color set for the filled slots. I don't like how this is declared. More info in color_wheel() declaration
var color_2 = [0.679, 0.405, 0.669,1];
@@ -82,6 +83,8 @@ var unique_names = false; // When enabled, force names to be unique when renam
var use_uid = 0; // Generating UID for each presets when enabled. Requires a [pattr preset_metadata]
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 poll_edited = 1; // If >0, check if current preset is edited every X seconds defined by the variable value.
var nbslot_edit = true; // If nbslot_edit and scrollable are enabled, the last two visible slots are replaced by buttons to add or remove lines of slot.
// (WORK)
var pattrstorage_name, pattrstorage_obj = null;
@@ -90,10 +93,12 @@ var columns, rows = 0;
var slots = []; // Stores on screen box, name, lock and interpolation state for all slots
var slots_highest = 0; // Highest filled preset slot number
var slots_count_display = 0; // Number of slots to be displayed
var true_slots_count_display = 0; // Number of slots to be displayed, including 2 extra slots for nbslot_edit buttons used as buttons instead of preset slots
var filled_slots = []; // List of stored slots
var filled_slots_dict = new Dict();
var active_slot = 0; //Last recalled slot
var active_slot_edited = false; //Active slot edited state flag. See run_edited_poll_task()
var previous_active_slot = 0; //Previously recalled slot
var previous_target = 0; //Here to deal with ongoing interpolations
var selected_slot = 0; //Last selected slot. Relevant especially when select_mode = 1. Otherwise it is the same as active_slot
@@ -112,6 +117,7 @@ var is_painting_base = 0;
var half_slot_size, half_margin, half_spacing;
var last_x, last_y, last_hovered = -1;
var last_hovered_is_preset_slot = false;
var y_offset = 0; // handle scrolling
var drag_scroll = 0; // handle scrolling when dragging outside of boundaries
var shift_hold, option_hold = 0;
@@ -133,6 +139,8 @@ var textedit_obj = null;
var textedit_initstate = {};
var is_typing_name = false;
var poll_edited_task = new Task(do_poll_edited, this);
var has_loaded = false;
if (jsarguments.length>1) { // Depreciated, use "pattrstorage" attribute instead of jsarguments.
@@ -195,20 +203,36 @@ function calc_rows_columns() {
slots[0] = new slot(0, 0, 0, 0, "(tmp)", 0, -1, 0, stored_slot_color); // Slot 0 is valid, but not represented in the GUI (and never saved by pattrstorage)
var minus_slots_carry = 0;
if (layout == 0) {
columns = Math.floor((ui_width - margin + spacing) / (slot_size + spacing));
rows = Math.floor((ui_height - margin + spacing) / (slot_size + spacing));
if (scrollable) {
rows = Math.max(rows, Math.max(min_rows, Math.ceil(slots_highest/columns)));
min_rows = Math.max(rows, min_rows);
if (nbslot_edit) {
rows += columns >= 2 ? 1 : 2;
if (columns > 2) {
minus_slots_carry = columns - 2;
}
}
}
} else {
columns = 1;
rows = Math.floor((ui_height - margin + spacing) / (slot_size + spacing));
min_rows = Math.max(rows, min_rows);
if (scrollable) {
rows = Math.max(rows, Math.max(min_rows, slots_highest));
if (nbslot_edit) {
rows += 2;
}
}
}
slots_count_display = columns * rows;
if (scrollable && nbslot_edit) {
}
true_slots_count_display = columns * rows - minus_slots_carry;
slots_count_display = scrollable && nbslot_edit ? true_slots_count_display - 2 : true_slots_count_display;
for (var i = 0; i < rows; i++) {
var top = margin + i * (spacing+slot_size);
@@ -267,16 +291,39 @@ function draw_slot(id, scale, cont) {
// slot name
cont.set_font_size(font_size*scale);
var text = format_slot_name(id);
var text;
if (scrollable && nbslot_edit && (id > (true_slots_count_display - 2))) {
if (id == true_slots_count_display - 1) {
text = "remove slot";
} else {
text = "add slot";
}
} else {
text = format_slot_name(id);
}
if (is_painting_base) {
draw_text_bubble(bg_txt_pos_x * scale, bg_txt_pos_y * scale, bg_txt_dim_w * scale, bg_txt_dim_h * scale, text, cont);
} else {
draw_text_bubble(bg_txt_pos_x + offset, bg_txt_pos_y + offset, bg_txt_dim_w * scale, bg_txt_dim_h * scale, text, cont);
}
}
}
if (scrollable && nbslot_edit) {
cont.set_line_width(Math.floor(slot_size * 0.3));
if (id > true_slots_count_display - 2) {
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.rel_line_to(slot_size * 0.8 * scale, 0);
cont.stroke();
}
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.rel_line_to(0, slot_size * 0.8 * scale);
cont.stroke();
}
}
}
draw_slot.local = 1;
@@ -288,7 +335,7 @@ function draw_slot_bubble(x, y, w, h, cont) {
cont.rectangle_rounded(x, y, w, h, slot_round_ratio * w, slot_round_ratio * h);
} else {
cont.rectangle(x, y, w, h);
}
}
}
draw_slot_bubble.local = 1;
@@ -342,7 +389,7 @@ function paint_base() {
set_font_size(font_size);
// All slots
for (var i = 1; i <= 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 (slots[i].name != null) {
if (color_mode == 1) {
@@ -416,7 +463,7 @@ function paint()
}
//Hide dragged slot
if (is_dragging) {
if (is_dragging) {
set_source_rgba(empty_slot_color);
draw_slot_bubble(slots[drag_slot].left, slots[drag_slot].top, slot_size, slot_size);
fill();
@@ -445,6 +492,14 @@ function paint()
}
}
//Edited dot
if (active_slot_edited && active_slot > 0 && selected_slot <= slots_count_display) {
post("draw edited dot\n");
set_source_rgba(edited_color);
ellipse(slots[active_slot].left + 1, slots[active_slot].top + 1, slot_size/3, slot_size/3);
fill();
}
// Hovered slot
if (last_hovered > -1) {
if (shift_hold) {
@@ -468,7 +523,16 @@ function paint()
if (layout == 0) {
//Text (slot number and name)
var text = format_slot_name(last_hovered);
var text = null;
if (scrollable && nbslot_edit && (last_hovered > (true_slots_count_display - 2))) {
if (last_hovered == true_slots_count_display - 1) {
text = "remove slot row";
} else {
text = "add slot row";
}
} else {
text = format_slot_name(last_hovered);
}
var text_dim = text_measure(text);
// If the text is too big or a slot is being dragged, display the text on top of the next slot.
// Otherwise, it gets displayed on the hovered slot.
@@ -913,20 +977,26 @@ function recall() {
} else {
previous_active_slot = previous_target;
}
set_active_slot(src_slot);
active_slot_edited = 0;
run_edited_poll_task();
} else if (interp == 1.0) {
slots[src_slot].interp = -1;
slots[trg_slot].interp = -1;
is_interpolating = 0;
previous_target = trg_slot;
set_active_slot(trg_slot);
active_slot_edited = 0;
run_edited_poll_task();
} else {
slots[src_slot].interp = 1 - interp;
slots[trg_slot].interp = interp;
is_interpolating = 1;
active_slot = 0;
// if (src_slot != trg_slot) {
// active_slot_edited = 1;
// cancel_edited_poll_task();
// }
}
if (recall_passthrough) {
outlet(0, "recall", src_slot, trg_slot, interp);
@@ -968,6 +1038,8 @@ function recallmulti() {
}
is_interpolating = 1;
active_slot_edited = 0;
run_edited_poll_task();
mgraphics.redraw();
outlet(0, "recallmulti", args);
@@ -1006,6 +1078,9 @@ function store(v) {
to_pattrstorage("store", v);
to_pattrstorage("getslotlist");
active_slot_edited = 0;
run_edited_poll_task();
if (recalc_rows_flag) {
calc_rows_columns();
} else {
@@ -1186,7 +1261,7 @@ slots_clear.local = 1;
function get_slot_index(x, y) {
// Returns which slot is hovered by the mouse
for (var i = 1; i <= slots_count_display; i++) {
for (var i = 1; i <= true_slots_count_display; i++) {
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)) {
return i;
@@ -1389,6 +1464,7 @@ function onidle(x,y,but,cmd,shift,capslock,option,ctrl)
option_hold = option;
redraw_flag = true;
}
last_hovered_is_preset_slot = scrollable && nbslot_edit && last_hovered > (true_slots_count_display - 2) ? false : true;
if (redraw_flag) {
mgraphics.redraw();
}
@@ -1409,6 +1485,19 @@ onidleout.local = 1;
function onclick(x,y,but,cmd,shift,capslock,option,ctrl)
{
if (scrollable && nbslot_edit) {
if (last_hovered == true_slots_count_display - 1) {
setmin_rows(min_rows-1);
y_offset = -1 * (bg_height - ui_height);
mgraphics.redraw();
return
} else if (last_hovered == true_slots_count_display) {
setmin_rows(min_rows+1);
y_offset = -1 * (bg_height - ui_height);
mgraphics.redraw();
return
}
}
if (is_typing_name) {
restore_textedit();
}
@@ -1485,9 +1574,13 @@ function ondrag(x,y,but,cmd,shift,capslock,option,ctrl)
} else if (is_dragging == 1) {
last_hovered = get_slot_index(x, y);
last_hovered_is_preset_slot = scrollable && nbslot_edit && last_hovered > (true_slots_count_display - 2) ? false : true;
if (!last_hovered_is_preset_slot) {
last_hovered = 0;
}
if (!but) {
// When the button is released, the dragging ceases
if (last_hovered > 0 && last_hovered != drag_slot) {
if (last_hovered > 0 && last_hovered != drag_slot && last_hovered_is_preset_slot) {
var cur_active_slot = active_slot;
var cur_prev_active_slot = previous_active_slot;
var offset = ((last_hovered <= drag_slot) && slots[last_hovered].name != null) ? 1 : 0;
@@ -1614,6 +1707,10 @@ function get_prect(prect) {
}
get_prect.local = 1;
function notifydeleted(){
poll_edited_task.freepeer();
}
// ATTRIBUTES DECLARATION
// If loaded in JSUI, only the first 4 arguments passed in declareattribute() will be used (no attribute styling). V8UI will also use the latest object argument.
declareattribute("pattrstorage", "getpattrstorage","setpattrstorage", 1, {type: "symbol", label: "Pattrstorage"});
@@ -2114,6 +2211,72 @@ function setui_rename(v){
}
}
declareattribute("poll_edited", "getpoll_edited", "setpoll_edited", 1, {type: "float", min: 0, label: "Poll Edited State"});
function getpoll_edited() {
return poll_edited;
}
function setpoll_edited(v){
poll_edited = v == 0 ? 0 : Math.max(0.1, Math.abs(v));
if (poll_edited > 0) {
run_edited_poll_task();
} else {
cancel_edited_poll_task();
}
}
function run_edited_poll_task() {
if (poll_edited_task.valid && !poll_edited_task.running && poll_edited > 0 && active_slot > 0) {
poll_edited_task.interval = poll_edited * 1000;
post("run task!\n");
poll_edited_task.repeat();
}
}
run_edited_poll_task.local = 1;
function cancel_edited_poll_task() {
poll_edited_task.cancel();
}
cancel_edited_poll_task.local = 1;
function do_poll_edited() {
if (pattrstorage_obj != null) {
to_pattrstorage("getedited");
}
}
do_poll_edited.local = 1;
declareattribute("edited_color", "getedited_color", "setedited_color", 1, {style: "rgba", label: "Edited Dot Color", category: "Appearance"});
function getedited_color() {
return edited_color;
}
function setedited_color(){
if (arguments.length == 4) {
edited_color = [arguments[0], arguments[1], arguments[2], arguments[3]];
} else if (arguments.length == 0) {
edited_color = [1, 0.49, 0.263, 1];
} else {
error('edited_color: wrong number of arguments\n');
}
}
function edited(v) {
active_slot_edited = v;
if (v == 1) {
cancel_edited_poll_task();
mgraphics.redraw();
}
}
declareattribute("nbslot_edit", "getnbslot_edit", "setnbslot_edit", 1, {style: "onoff", label: "Rename In UI"});
function getnbslot_edit() {
return nbslot_edit;
}
function setnbslot_edit(v){
nbslot_edit = v > 0;
y_offset = 0;
calc_rows_columns();
}
// UTILITY
function post_keys(obj) {
post('Keys of obj: ', obj, '\n');