From d269e93d9344162bdbaf9830d6aa34c108a77a9b Mon Sep 17 00:00:00 2001 From: Jonathan Wilkes <jon.w.wilkes@gmail.com> Date: Wed, 4 Jan 2017 22:44:29 -0500 Subject: [PATCH] fix bugs with grid, add a rudimentary external dialog interface for it and other externals --- externals/unauthorized/grid/grid.c | 90 ++++++++++--- pd/nw/dialog_external.html | 204 +++++++++++++++++++++++++++++ pd/nw/pdgui.js | 77 +++++++---- 3 files changed, 325 insertions(+), 46 deletions(-) create mode 100644 pd/nw/dialog_external.html diff --git a/externals/unauthorized/grid/grid.c b/externals/unauthorized/grid/grid.c index ac814f953..2349ce64d 100644 --- a/externals/unauthorized/grid/grid.c +++ b/externals/unauthorized/grid/grid.c @@ -99,7 +99,8 @@ static void grid_draw_update(t_grid *x, t_glist *glist) // } } -static void grid_draw_new(t_grid *x, t_glist *glist) +static void grid_draw_select(t_grid* x, t_glist* glist); +static void grid_draw_configure(t_grid *x, t_glist *glist) { t_canvas *canvas=glist_getcanvas(glist); //GRID_SYS_VGUI8(".x%lx.c create rectangle %d %d %d %d " @@ -148,14 +149,7 @@ static void grid_draw_new(t_grid *x, t_glist *glist) //} // gui_gobj_new, "xx type text_xpix text_ypix istoplevel" - gui_vmess("gui_gobj_new", "xxsiii", - canvas, - x, - "obj", - text_xpix(&x->x_obj, glist), - text_ypix(&x->x_obj, glist), - glist_istoplevel(glist)); - gui_vmess("gui_grid_draw_bg", "xxiisiii", + gui_vmess("gui_configure_grid", "xxiisiii", canvas, x, x->x_width, @@ -164,9 +158,23 @@ static void grid_draw_new(t_grid *x, t_glist *glist) x->x_grid, x->x_xlines, x->x_ylines); + if (glist_isselected(glist, &x->x_obj)) + grid_draw_select(x, glist); canvas_fixlinesfor( canvas, (t_text*)x ); } +static void grid_draw_new(t_grid *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + gui_vmess("gui_grid_new", "xxiii", + canvas, + x, + text_xpix(&x->x_obj, glist), + text_ypix(&x->x_obj, glist), + glist_istoplevel(glist)); + grid_draw_configure(x, glist); +} + static void grid_draw_move(t_grid *x, t_glist *glist) { t_canvas *canvas=glist_getcanvas(glist); @@ -323,14 +331,36 @@ static void grid_save(t_gobj *z, t_binbuf *b) static void grid_properties(t_gobj *z, t_glist *owner) { char buf[800]; + char *gfx_tag; t_grid *x=(t_grid *)z; - sprintf(buf, "pdtk_grid_dialog %%s %s %d %.2f %.2f %d %.2f %.2f %d %.2f %.2f %d %d\n", - x->x_name->s_name, x->x_width, x->x_min, x->x_max, x->x_height, - x->y_min, x->y_max, x->x_grid, x->x_xstep, x->x_ystep, - x->x_xlines, x->x_ylines ); + //sprintf(buf, "pdtk_grid_dialog %%s %s %d %.2f %.2f %d %.2f %.2f %d %.2f %.2f %d %d\n", + // x->x_name->s_name, x->x_width, x->x_min, x->x_max, x->x_height, + // x->y_min, x->y_max, x->x_grid, x->x_xstep, x->x_ystep, + // x->x_xlines, x->x_ylines ); // post("grid_properties : %s", buf ); - gfxstub_new(&x->x_obj.ob_pd, x, buf); + //gfxstub_new(&x->x_obj.ob_pd, x, buf); + gfx_tag = gfxstub_new2(&x->x_obj.ob_pd, x); + + gui_start_vmess("gui_external_dialog", "ss", gfx_tag, "grid"); + gui_start_array(); + + gui_s("receive_symbol"); gui_s(x->x_name->s_name); + gui_s("width"); gui_i(x->x_width); + gui_s("min"); gui_f(x->x_min); + gui_s("max"); gui_f(x->x_max); + gui_s("height"); gui_i(x->x_height); + gui_s("y-min"); gui_f(x->y_min); + gui_s("y-max"); gui_f(x->y_max); + gui_s("grid_toggle"); gui_i(x->x_grid); + gui_s("x-steps"); gui_f(x->x_xstep); + gui_s("y-steps"); gui_f(x->x_ystep); + gui_s("x-lines"); gui_i(x->x_xlines); + gui_s("y-lines"); gui_i(x->x_ylines); + + gui_end_array(); + gui_end_vmess(); + } static void grid_select(t_gobj *z, t_glist *glist, int selected) @@ -390,8 +420,9 @@ static void grid_dialog(t_grid *x, t_symbol *s, int argc, t_atom *argv) x->x_ystep = argv[9].a_w.w_float; x->x_xlines = argv[10].a_w.w_float; x->x_ylines = argv[11].a_w.w_float; - grid_draw_erase(x, x->x_glist); - grid_draw_new(x, x->x_glist); + //grid_draw_erase(x, x->x_glist); + //grid_draw_new(x, x->x_glist); + grid_draw_configure(x, x->x_glist); } static void grid_delete(t_gobj *z, t_glist *glist) @@ -418,6 +449,26 @@ static void grid_displace(t_gobj *z, t_glist *glist, int dx, int dy) } } +static void grid_displace_wtag(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_grid *x = (t_grid *)z; + int xold = text_xpix(&x->x_obj, glist); + int yold = text_ypix(&x->x_obj, glist); + + // post( "grid_displace dx=%d dy=%d", dx, dy ); + + x->x_obj.te_xpix += dx; + x->x_current += dx; + x->x_obj.te_ypix += dy; + x->y_current += dy; + if (xold != text_xpix(&x->x_obj, glist) || yold != text_ypix(&x->x_obj, glist)) + { + //grid_draw_move(x, x->x_glist); + canvas_fixlinesfor(glist, (t_text *)z); + } +} + + static void grid_motion(t_grid *x, t_floatarg dx, t_floatarg dy) { int xold = x->x_current; @@ -508,8 +559,9 @@ static void grid_new_color(t_grid *x, t_floatarg color1, sprintf(col3,"%X",(int) color3); sprintf( x->x_bgcolor, "#%s%s%s", col1, col2, col3); - grid_draw_erase(x, x->x_glist); - grid_draw_new(x, x->x_glist); + //grid_draw_erase(x, x->x_glist); + //grid_draw_new(x, x->x_glist); + grid_draw_configure(x, x->x_glist); } static void grid_values(t_grid* x, t_floatarg xvalue, t_floatarg yvalue) @@ -724,6 +776,7 @@ static t_grid *grid_new(t_symbol *s, int argc, t_atom *argv) static void grid_free(t_grid *x) { pd_unbind(&x->x_obj.ob_pd, x->x_name); + gfxstub_deleteforkey(x); } void grid_setup(void) @@ -759,6 +812,7 @@ void grid_setup(void) grid_widgetbehavior.w_deletefn = grid_delete; grid_widgetbehavior.w_visfn = grid_vis; grid_widgetbehavior.w_clickfn = grid_click; + grid_widgetbehavior.w_displacefnwtag = grid_displace_wtag; #if PD_MINOR_VERSION >= 37 class_setpropertiesfn(grid_class, grid_properties); diff --git a/pd/nw/dialog_external.html b/pd/nw/dialog_external.html new file mode 100644 index 000000000..63b31d643 --- /dev/null +++ b/pd/nw/dialog_external.html @@ -0,0 +1,204 @@ +<!DOCTYPE html> +<html> + <head> + <link id="page_style" rel="stylesheet" type="text/css" href="css/default.css"> + </head> + <body class="dialog_body"> + <div class="container"> + <form> + <fieldset> + <legend></legend> + </fieldset> + <div class="submit_buttons"> + <button type="button" + onClick="ok();" + id="ok_button" + data-i18n="[title]iem.prop.ok_tt"> + <span data-i18n="iem.prop.ok"></span> + </button> + <button type="button" + onClick="apply();" + id="apply_button" + data-i18n="[title]iem.prop.apply_tt"> + <span data-i18n="iem.prop.apply"></span> + </button> + <button type="button" + onClick="cancel(true);" + data-i18n="[title]iem.prop.cancel_tt"> + <span data-i18n="iem.prop.cancel"></span> + </button> + </div> + </form> + </div> + <script> +"use strict"; +var gui = require("nw.gui"); +var pdgui = require("./pdgui.js"); + +// For translations +var l = pdgui.get_local_string; + +pdgui.skin.apply(window); + +var pd_object_callback; +var old_properties; // Original values in case the user wants to cancel changes +var new_properties; // Updated values + +// Grab focus for one of the buttons +function focus_button(id) { + document.getElementById(id).focus(); +} + +function send_props_to_pd(attr_array) { + var values = []; + // Todo: parse symbols the same way we do for iemguis before + // sending them back to Pd. This might require some fanagling on + // the pd side of things... + attr_array.forEach(function(elem) { + values.push(elem.value); + }); + pdgui.pdsend(pd_object_callback, "dialog", values.join(" ")); +} + +function apply() { + focus_button("apply_button"); + send_props_to_pd(new_properties); +} + +function cancel() { + send_props_to_pd(old_properties); + pdgui.pdsend(pd_object_callback, "cancel"); +} + +function ok() { + // Focus button so that the "change" event triggers + // for the form before sending values to Pd + focus_button("ok_button"); + send_props_to_pd(new_properties); + pdgui.pdsend(pd_object_callback, "cancel"); +} + +// turn a ["name", value, etc.] array +// into an array of objects where each object contains +// name: name for the input element +// type: type of element (number, string, toggle) +// label: string label (basically the name with underscores turned to spaces) +// value: a value for the input +function parse_attrs(attrs) { + var ret = [], + elem; + attrs.forEach(function(token, i) { + if (i % 2 === 0) { + elem = {}; + token = token.split("_"); + if (token.length > 1) { + elem.type = token[token.length - 1]; + if (elem.type !== "symbol" && + elem.type !== "toggle") { + // no suffix defaults to "number" + elem.type = "number"; + } else { + // remove the type suffix + token = token.slice(0, -1); + } + } else { + elem.type = "number"; + } + elem.name = token.join("_"); + elem.label = token.join(" "); + } else { + elem.value = token; + // now push the object onto the array + ret.push(elem); + } + }); + return ret; +} + +// This gets called from the nw_create_window function in index.html +// It provides us with our window id from the C side. Once we have it +// we can create the menu and register event callbacks +function register_window_id(gfxstub, args) { + var external_name = args.name, + array_of_objects; + pd_object_callback = gfxstub; + array_of_objects = parse_attrs(args.attributes); + // Store our array for later use + new_properties = array_of_objects; + // Also make a copy in the case that the user cancels this dialog + old_properties = JSON.parse(JSON.stringify(array_of_objects)); + + add_events(gfxstub); + // not sure that we need this for properties windows + //pdgui.canvas_map(gfxstub); + translate_form(); + + build_form(external_name, array_of_objects); + + // We don't turn on rendering of the "container" div until + // We've finished displaying all the spans and populating the + // labels and form elements. That makes it more efficient and + // snappier, at least on older machines. + document.getElementsByClassName("container")[0] + .style.setProperty("display", "inline"); +} + +// Stop-gap translator +function translate_form() { + var elements = document.querySelectorAll("[data-i18n]"), + data, + i; + for (i = 0; i < elements.length; i++) { + data = elements[i].dataset.i18n; + if (data.slice(0, 7) === "[title]") { + elements[i].title = l(data.slice(7)); + } else { + elements[i].textContent = l(data); + } + } +} + +function get_input_type(t) { + return t === "symbol" ? "text" : + t === "number" ? "text" : + t === "toggle" ? "checkbox": + "text"; +} + +function build_form(external_name, array_of_objects) { + var fieldset = document.querySelector("fieldset"); + document.querySelector("legend").textContent = external_name; + array_of_objects.forEach(function(elem) { + var input_elem = document.createElement("input"), + label = document.createElement("label"); + input_elem.type = get_input_type(elem.type); + if (input_elem.type === "checkbox") { + input_elem.checked = elem.value !== 0; + input_elem.onchange = function() { + elem.value = input_elem.checked ? 1 : 0; + }; + } else { + input_elem.value = elem.value; + input_elem.onchange = function() { + elem.value = input_elem.value; + } + } + label.textContent = elem.label; + label.appendChild(input_elem); + fieldset.appendChild(label); + // stop-gap until we make this prettier through css: insert a break + fieldset.appendChild(document.createElement("br")); + }); +} + +function add_events(name) { + // closing the Window + gui.Window.get().on("close", function () { + // this needs to do whatever the "cancel" button does + cancel(); + }); + pdgui.dialog_bindings(name); +} + </script> + </body> +</html> diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index fd08f341e..9f3d3d930 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -3596,7 +3596,7 @@ function gui_scope_displace(cid, tag, dx, dy) { // unauthorized/grid -function make_grid_data(w, h, x_l, y_l) { +function get_grid_data(w, h, x_l, y_l) { var d, x, y, offset; d = []; offset = Math.floor(w / x_l); @@ -3609,7 +3609,7 @@ function make_grid_data(w, h, x_l, y_l) { } offset = Math.floor(h / y_l); if (offset > 0) { - for (y = 0; y < w; y += offset) { + for (y = 0; y < h; y += offset) { d = d.concat(["M", 0, y, w, y]); // horizontal line } } else { @@ -3618,23 +3618,17 @@ function make_grid_data(w, h, x_l, y_l) { return d.join(" "); } -function gui_grid_draw_bg(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { +function gui_configure_grid(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { var g = get_gobj(cid, tag), - border, // for inheriting gui preset colors - bg, - out_0, - out_1, - grid, + // configure each element in the grid grid_d_string, - point, point_size = 5; - bg = create_item(cid, "rect", { + configure_item(g.querySelector(".bg"), { width: w, height: h, fill: bg_color, - class: "bg" }); - border = create_item(cid, "path", { + configure_item(g.querySelector(".border"), { d: ["M", 0, 0, w, 0, "M", 0, h, w, h, "M", 0, 0, 0, h, @@ -3645,18 +3639,16 @@ function gui_grid_draw_bg(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { fill: "none", stroke: "black", "stroke-width": 1, - class: "border" // now we can inherit the css border styles }); - out_0 = create_item(cid, "rect", { + configure_item(g.querySelector(".out_0"), { y: h + 1, width: 7, height: 1, fill: "none", stroke: "black", "stroke-width": 1, - class: "out_0" }); - out_1 = create_item(cid, "rect", { + configure_item(g.querySelector(".out_1"), { x: w - 7, y: h + 1, width: 7, @@ -3664,26 +3656,45 @@ function gui_grid_draw_bg(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { fill: "none", stroke: "black", "stroke-width": 1, - class: "out_1" }); - if (has_grid === 1) { - grid_d_string = make_grid_data(w, h, x_l, y_l); - grid = create_item(cid, "path", { - d: grid_d_string, - stroke: "white", - "stroke-width": 1, - class: "grid" - }); - } - point = create_item(cid, "rect", { + + grid_d_string = !!has_grid ? get_grid_data(w, h, x_l, y_l) : ""; + configure_item(g.querySelector(".grid"), { + d: grid_d_string, + stroke: "white", + "stroke-width": 1, + }); + + configure_item(g.querySelector(".point"), { style: "visibility: none;", width: 5, height: 5, fill: "#ff0000", stroke: "black", "stroke-width": 1, - class: "point" }); +} + +function gui_grid_new(cid, tag, x, y, is_toplevel) { + var g = gui_gobj_new(cid, tag, "obj", x, y, is_toplevel), + bg = create_item(cid, "rect", { + class: "bg" + }), + border = create_item(cid, "path", { + class: "border" // now we can inherit the css border styles + }), + out_0 = create_item(cid, "rect", { + class: "out_0" + }), + out_1 = create_item(cid, "rect", { + class: "out_1" + }), + grid = create_item(cid, "path", { + class: "grid" + }), + point = create_item(cid, "rect", { + class: "point" + }); g.appendChild(bg); g.appendChild(out_0); g.appendChild(out_1); @@ -3692,6 +3703,7 @@ function gui_grid_draw_bg(cid, tag, w, h, bg_color, has_grid, x_l, y_l) { g.appendChild(border); } + function gui_grid_point(cid, tag, x, y) { var g = get_gobj(cid, tag), p = g.querySelector(".point"); @@ -4331,6 +4343,15 @@ function gui_font_dialog(cid, gfxstub, font_size) { attrs); } +function gui_external_dialog(did, external_name, attr_array) { + create_window(did, "external", 265, 450, + popup_coords[2], popup_coords[3], + { + name: external_name, + attributes: attr_array + }); +} + // Global settings function gui_pd_dsp(state) { -- GitLab