From 3cf747978ab5704ac40191e5a03fe96055fc1152 Mon Sep 17 00:00:00 2001 From: user <user@user-ThinkPad-X60.(none)> Date: Fri, 22 May 2015 12:36:03 -0400 Subject: [PATCH] got a very early working version that does _all_ object text editing (arrow keys, selection, etc.) in the GUI. (However, it still depends on g_rtext.c for doing everything else.) --- pd/nw/pd_canvas.html | 325 +++++++++++++++++++++++++------------------ pd/nw/pdgui.js | 41 +++++- pd/nw/todo.txt | 24 ++++ pd/src/g_canvas.h | 1 + pd/src/g_editor.c | 24 ++++ pd/src/g_rtext.c | 25 +++- pd/src/g_text.c | 6 +- 7 files changed, 294 insertions(+), 152 deletions(-) diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html index 80ab73882..46c16ce03 100644 --- a/pd/nw/pd_canvas.html +++ b/pd/nw/pd_canvas.html @@ -43,120 +43,155 @@ function add_keymods(key, evt) { var canvas_events = (function() { var name, - mousemove = function(evt) { - //pdgui.gui_post("x: " + evt.pageX + " y: " + evt.pageY + - // " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1))); - pdgui.pdsend(name + - " motion " + evt.pageX + " " + evt.pageY + " " + - (evt.shiftKey + (evt.ctrlKey << 1))); - evt.stopPropagation(); - evt.preventDefault(); - return false; + textbox = function () { + return document.getElementById('new_object_textentry'); }, - mousedown = function(evt) { - // tk events are one greater than html5... - var b = evt.button + 1; - var mod; - // For some reason right-click sends a modifier value of "8", - // and canvas_doclick in g_editor.c depends on that value to - // do the right thing. So let's hack... - if (b === 3) { // right-click - mod = 8; - } else { - mod = (evt.shiftKey + (evt.ctrlKey << 1)); - } - pdgui.pdsend(name + " mouse " + evt.pageX + " " + evt.pageY + " " + - b + " " + mod); - //evt.stopPropagation(); - evt.preventDefault(); - }, - mouseup = function(evt) { - //pdgui.gui_post("mouseup: x: " + evt.pageX + " y: " + evt.pageY + - // " button: " + (evt.button + 1)); - pdgui.pdsend(name + - " mouseup " + evt.pageX + " " + evt.pageY + " " + - (evt.button + 1)); - evt.stopPropagation(); - evt.preventDefault(); - }, - keydown = function(evt) { - var key_code = evt.keyCode; - var hack = null; // hack for unprintable ascii codes - switch(key_code) { - case 8: - case 9: - case 10: - case 27: - //case 32: - case 127: hack = key_code; break; - case 37: hack = add_keymods('Left', evt); break; - case 38: hack = add_keymods('Up', evt); break; - case 39: hack = add_keymods('Right', evt); break; - case 40: hack = add_keymods('Down', evt); break; - case 33: hack = add_keymods('Prior', evt); break; - case 34: hack = add_keymods('Next', evt); break; - case 35: hack = add_keymods('End', evt); break; - case 36: hack = add_keymods('Home', evt); break; - - // Need to handle Control key, Alt - - case 16: hack = 'Shift'; break; - case 17: hack = 'Control'; break; - case 18: hack = 'Alt'; break; - } - if (hack !== null) { - pdgui.gui_canvas_sendkey(name, 1, evt, hack); - pdgui.set_keymap(key_code, hack); + events = { + mousemove: function(evt) { + //pdgui.gui_post("x: " + evt.pageX + " y: " + evt.pageY + + // " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1))); + pdgui.pdsend(name + + " motion " + evt.pageX + " " + evt.pageY + " " + + (evt.shiftKey + (evt.ctrlKey << 1))); + evt.stopPropagation(); + evt.preventDefault(); + return false; + }, + mousedown: function(evt) { + // tk events are one greater than html5... + var b = evt.button + 1; + var mod; + // For some reason right-click sends a modifier value of "8", + // and canvas_doclick in g_editor.c depends on that value to + // do the right thing. So let's hack... + if (b === 3) { // right-click + mod = 8; + } else { + mod = (evt.shiftKey + (evt.ctrlKey << 1)); + } + pdgui.pdsend(name + + " mouse " + evt.pageX + " " + evt.pageY + " " + + b + " " + mod); + //evt.stopPropagation(); + evt.preventDefault(); + }, + mouseup: function(evt) { + //pdgui.gui_post("mouseup: x: " + + // evt.pageX + " y: " + evt.pageY + + // " button: " + (evt.button + 1)); + pdgui.pdsend(name + + " mouseup " + evt.pageX + " " + evt.pageY + " " + + (evt.button + 1)); + evt.stopPropagation(); + evt.preventDefault(); + }, + keydown: function(evt) { + var key_code = evt.keyCode; + var hack = null; // hack for unprintable ascii codes + switch(key_code) { + case 8: + case 9: + case 10: + case 27: + //case 32: + case 127: hack = key_code; break; + case 37: hack = add_keymods('Left', evt); break; + case 38: hack = add_keymods('Up', evt); break; + case 39: hack = add_keymods('Right', evt); break; + case 40: hack = add_keymods('Down', evt); break; + case 33: hack = add_keymods('Prior', evt); break; + case 34: hack = add_keymods('Next', evt); break; + case 35: hack = add_keymods('End', evt); break; + case 36: hack = add_keymods('Home', evt); break; + + // Need to handle Control key, Alt + + case 16: hack = 'Shift'; break; + case 17: hack = 'Control'; break; + case 18: hack = 'Alt'; break; + } + if (hack !== null) { + pdgui.gui_canvas_sendkey(name, 1, evt, hack); + pdgui.set_keymap(key_code, hack); + } + pdgui.gui_post("keydown time: keycode is " + evt.keyCode); + last_keydown = evt.keyCode; + //evt.stopPropagation(); + //evt.preventDefault(); + }, + keypress: function(evt) { + pdgui.gui_canvas_sendkey(name, 1, evt, evt.charCode); + pdgui.set_keymap(last_keydown, evt.charCode); + pdgui.gui_post("keypress time: charcode is " + evt.charCode); + // Don't do things like scrolling on space, arrow keys, etc. + //evt.stopPropagation(); + evt.preventDefault(); + }, + keyup: function(evt) { + var my_char_code = pdgui.get_char_code(evt.keyCode); + pdgui.gui_canvas_sendkey(name, 0, evt, my_char_code); + pdgui.gui_post("keyup time: charcode is: " + my_char_code); + evt.stopPropagation(); + evt.preventDefault(); + }, + text_mousemove: function(evt) { // we may not need this one + evt.stopPropagation(); + //evt.preventDefault(); + return false; + }, + text_mousedown: function(evt) { + if (textbox() !== evt.target) { + pdgui.gui_post("my content was " + textbox().textContent); + pdgui.pdsend(name + " stringforobj " + + textbox().textContent); + events.mousedown(evt); + canvas_events.normal(); + } + evt.stopPropagation(); + //evt.preventDefault(); + return false; + }, + text_mouseup: function(evt) { + pdgui.gui_post("mouseup target is " + + evt.target + " and textbox is " + textbox()); + // if (evt.target === textbox) { + // pdgui.gui_post("it's a mouseup in a textbox"); + // } else { + // pdgui.gui_post("it's a mouseup outside a textbox"); + // } + // evt.stopPropagation(); + //evt.preventDefault(); + return false; + }, + text_keydown: function(evt) { + evt.stopPropagation(); + //evt.preventDefault(); + return false; + }, + text_keyup: function(evt) { + evt.stopPropagation(); + //evt.preventDefault(); + return false; + }, + text_keypress: function(evt) { + evt.stopPropagation(); + //evt.preventDefault(); + return false; + }, + floating_text_click: function(evt) { + pdgui.gui_post("leaving floating mode"); + canvas_events.text(); + evt.stopPropagation(); + evt.preventDefault(); + return false; + }, + floating_text_keypress: function(evt) { + pdgui.gui_post("leaving floating mode"); + canvas_events.text(); + evt.stopPropagation(); + evt.preventDefault(); + return false; } - pdgui.gui_post("keydown time: keycode is " + evt.keyCode); - last_keydown = evt.keyCode; - //evt.stopPropagation(); - //evt.preventDefault(); - }, - keypress = function(evt) { - pdgui.gui_canvas_sendkey(name, 1, evt, evt.charCode); - pdgui.set_keymap(last_keydown, evt.charCode); - pdgui.gui_post("keypress time: charcode is " + evt.charCode); - // Don't do things like scrolling on space, arrow keys, etc. - //evt.stopPropagation(); - evt.preventDefault(); - }, - keyup = function(evt) { - var my_char_code = pdgui.get_char_code(evt.keyCode); - pdgui.gui_canvas_sendkey(name, 0, evt, my_char_code); - pdgui.gui_post("keyup time: charcode is: " + my_char_code); - evt.stopPropagation(); - evt.preventDefault(); - }, - text_mousemove = function(evt) { - evt.stopPropagation(); - //evt.preventDefault(); - return false; - }, - text_mousedown = function(evt) { - evt.stopPropagation(); - //evt.preventDefault(); - return false; - }, - text_mouseup = function(evt) { - evt.stopPropagation(); - //evt.preventDefault(); - return false; - }, - text_keydown = function(evt) { - evt.stopPropagation(); - //evt.preventDefault(); - return false; - }, - text_keyup = function(evt) { - evt.stopPropagation(); - //evt.preventDefault(); - return false; - }, - text_keypress = function(evt) { - evt.stopPropagation(); - //evt.preventDefault(); - return false; } ; @@ -195,7 +230,6 @@ var canvas_events = (function() { }, false ); - // closing the Window // this isn't actually closing the window yet nw.Window.get().on("close", function() { @@ -203,35 +237,48 @@ var canvas_events = (function() { }); return { + none: function() { + var name; + for (var prop in events) { + if (events.hasOwnProperty(prop)) { + name = prop.split('_'); + name = name[name.length -1]; + document.removeEventListener(name, events[prop], false); + } + } + }, normal: function() { - document.removeEventListener("mousemove", text_mousemove, false); - document.removeEventListener("keydown", text_keydown, false); - document.removeEventListener("keypress", text_keypress, false); - document.removeEventListener("keyup", text_keyup, false); - document.removeEventListener("mousedown", text_mousedown, false); - document.removeEventListener("mouseup", text_mouseup, false); - - document.addEventListener("mousemove", mousemove, false); - document.addEventListener("keydown", keydown, false); - document.addEventListener("keypress", keypress, false); - document.addEventListener("keyup", keyup, false); - document.addEventListener("mousedown", mousedown, false); - document.addEventListener("mouseup", mouseup, false); + pdgui.gui_post("resetting to normal..."); + this.none(); + + document.addEventListener("mousemove", events.mousemove, false); + document.addEventListener("keydown", events.keydown, false); + document.addEventListener("keypress", events.keypress, false); + document.addEventListener("keyup", events.keyup, false); + document.addEventListener("mousedown", events.mousedown, false); + document.addEventListener("mouseup", events.mouseup, false); }, text: function() { - document.removeEventListener("mousemove", mousemove, false); - document.removeEventListener("keydown", keydown, false); - document.removeEventListener("keypress", keypress, false); - document.removeEventListener("keyup", keyup, false); - document.removeEventListener("mousedown", mousedown, false); - document.removeEventListener("mouseup", mouseup, false); - - document.addEventListener("mousemove", text_mousemove, false); - document.addEventListener("keydown", text_keydown, false); - document.addEventListener("keypress", text_keypress, false); - document.addEventListener("keyup", text_keyup, false); - document.addEventListener("mousedown", text_mousedown, false); - document.addEventListener("mouseup", mouseup, false); + this.none(); + + document.addEventListener("mousemove", events.text_mousemove, false); + document.addEventListener("keydown", events.text_keydown, false); + document.addEventListener("keypress", events.text_keypress, false); + document.addEventListener("keyup", events.text_keyup, false); + document.addEventListener("mousedown", events.text_mousedown, false); + document.addEventListener("mouseup", events.text_mouseup, false); + }, + floating_text: function() { + this.none(); + this.text(); + document.removeEventListener("mousedown", events.text_mousedown, false); + document.removeEventListener("mouseup", events.text_mouseup, false); + document.removeEventListener("mousemove", events.text_mousemove, false); + document.removeEventListener("keypress", events.text_keypress, false); + document.addEventListener("click", events.floating_text_click, false); + + document.addEventListener("keypress", events.floating_text_keypress, false); + document.addEventListener("mousemove", events.mousemove, false); }, register: function(n) { name = n; diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index 2fd515699..fb2202b6b 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -2212,8 +2212,22 @@ function gui_text_displace(name, tag, dx, dy) { elem_displace(get_gobj(name, tag), dx, dy); } +function textentry_displace(t, dx, dy) { + var transform = t.style.getPropertyValue('transform') + .split('(')[1] // get everything after the '(' + .replace(')', '') // remove trailing ')' + .split(','); // split into x and y + var x = +transform[0].trim().replace('px', ''), + y = +transform[1].trim().replace('px', ''); +gui_post("x is " + x + " and y is " + y); + t.style.setProperty('transform', + 'translate(' + + (x + dx) + 'px, ' + + (y + dy) + 'px)'); +} + function gui_canvas_displace_withtag(name, dx, dy) { - var pwin = patchwin[name], i; + var pwin = patchwin[name], i, textentry; var ol = pwin.window.document.getElementsByClassName('selected'); for (i = 0; i < ol.length; i++) { elem_displace(ol[i], dx, dy); @@ -2223,6 +2237,11 @@ function gui_canvas_displace_withtag(name, dx, dy) { // elem.matrix.e = new_tx; // elem.matrix.f = new_ty; } + textentry = patchwin[name].window.document.getElementById('new_object_textentry'); + if (textentry !== null) { + textentry_displace(textentry, dx, dy); + } + // elem.setAttributeNS(null, 'transform', // 'translate(' + new_tx + ',' + new_ty + ')'); // } @@ -3396,9 +3415,10 @@ exports.skin = (function () { }; }()); -function gui_textarea(cid, tag, x, y, font_size, state) { +function gui_textarea(cid, tag, x, y, max_char_width, font_size, state) { gui_post("x/y is " + x + '/' + y); - if (state === 1) { + gui_post("state? " + state); + if (state !== 0) { var p = patchwin[cid].window.document.createElement('p'); configure_item(p, { id: 'new_object_textentry' @@ -3407,12 +3427,19 @@ function gui_textarea(cid, tag, x, y, font_size, state) { p.style.setProperty('left', x + 'px'); p.style.setProperty('top', y + 'px'); p.style.setProperty('font-size', font_size + 'px'); - p.textContent = "Fuck Butts"; + p.style.setProperty('transform', 'translate(0px, 0px)'); + p.style.setProperty('max-width', + max_char_width === 0 ? '60ch' : max_char_width + 'ch'); + p.style.setProperty('min-width', + max_char_width === 0 ? '3ch' : max_char_width + 'ch'); +// p.textContent = "Fuck Butts"; patchwin[cid].window.document.body.appendChild(p); p.focus(); - patchwin[cid].window.canvas_events.text(); - /* here we need to make sure that events inside the - <p> don't propogate down to the svg below... */ + if (state === 1) { + patchwin[cid].window.canvas_events.text(); + } else { + patchwin[cid].window.canvas_events.floating_text(); + } } else { var p = patchwin[cid].window.document.getElementById('new_object_textentry'); if (p !== null) { diff --git a/pd/nw/todo.txt b/pd/nw/todo.txt index 5e7d09edc..8bccae975 100644 --- a/pd/nw/todo.txt +++ b/pd/nw/todo.txt @@ -1,3 +1,26 @@ +Event settings for edit mode +---------------------------- + +1) No box - normal editmode behavior + * moving/selecting + * setting a bounding rect + * mouse cannot select text + * text is not editable + * clicking a box triggers #2 below +2) Editing a box in place + * x/y is set + * can type inside box + * can select text with the mouse + * mouse motion doesn't change box position + * applies to all boxes which already exist and are being edited +3) Editing a new box before it's anchored + * x/y follows mouse + * can type new text into the box + * a click in the box will anchor the box and trigger #2 above + +*need to be able to tell the difference between new obj and retexted +obj + Problems to put off until all (or most) sys_vgui calls are eliminated: 1) gui-side parser inside -- pdgui.js. Currently we're splitting on newlines so we can separate gui_vmess from sys_vgui calls. This makes it very difficult to handle multi-line msg and text @@ -150,6 +173,7 @@ Everything else: (A [x] means we've fixed it) \"%.6lx\". (Not exactly sure what good the "x" does there.) It's only specified in s_inter and in editor_new, so it should be easy to amend if need be. +[ ] make "rtext" textarea <div> static, and turn display on/off Crashers -------- diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h index 4337d37bd..63c28c0f7 100644 --- a/pd/src/g_canvas.h +++ b/pd/src/g_canvas.h @@ -509,6 +509,7 @@ EXTERN void rtext_mouse(t_rtext *x, int xval, int yval, int flag); //5 EXTERN void rtext_retext(t_rtext *x); //5 EXTERN char *rtext_gettag(t_rtext *x); //47 EXTERN void rtext_gettext(t_rtext *x, char **buf, int *bufsize); //9 +EXTERN void rtext_settext(t_rtext *x, char *buf, int bufsize); //1 EXTERN void rtext_getseltext(t_rtext *x, char **buf, int *bufsize); //4 /* -------------------- functions on canvases ------------------------ */ diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c index d900479fd..9c4650e9c 100644 --- a/pd/src/g_editor.c +++ b/pd/src/g_editor.c @@ -7613,6 +7613,28 @@ static void canvas_tip(t_canvas *x, t_symbol *s, int argc, t_atom *argv) } } +static void canvas_stringforobj(t_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ + int length; + char *buf; + t_gobj *y; + t_rtext *rtext; + if (!x->gl_editor || argc < 1) return; + for (y = x->gl_list; y; y = y->g_next) + { + if (glist_isselected(x, y) && (rtext = glist_findrtext(x, (t_text *)y))) + { + rtext_gettext(rtext, &buf, &length); + t_binbuf *b = binbuf_new(); + binbuf_add(b, argc, argv); + binbuf_gettext(b, &buf, &length); + rtext_settext(rtext, buf, length); + binbuf_free(b); + glist_deselect(x, y); + } + } +} + void g_editor_setup(void) { /* ------------------------ events ---------------------------------- */ @@ -7634,6 +7656,8 @@ void g_editor_setup(void) A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_tip, gensym("echo"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_stringforobj, + gensym("stringforobj"), A_GIMME, A_NULL); /* ------------------------ menu actions ---------------------------- */ class_addmethod(canvas_class, (t_method)canvas_menuclose, gensym("menuclose"), A_DEFFLOAT, 0); diff --git a/pd/src/g_rtext.c b/pd/src/g_rtext.c index c82c9be14..79b54200e 100644 --- a/pd/src/g_rtext.c +++ b/pd/src/g_rtext.c @@ -106,6 +106,13 @@ void rtext_gettext(t_rtext *x, char **buf, int *bufsize) *bufsize = x->x_bufsize; } +void rtext_settext(t_rtext *x, char *buf, int bufsize) +{ + freebytes(x->x_buf, x->x_bufsize); + x->x_buf = buf; + x->x_bufsize = bufsize; +} + void rtext_getseltext(t_rtext *x, char **buf, int *bufsize) { *buf = x->x_buf + x->x_selstart; @@ -553,7 +560,7 @@ post("selected an rtext"); void rtext_activate(t_rtext *x, int state) { fprintf(stderr,"rtext_activate state=%d\n", state); - int w = 0, h = 0, indx; + int w = 0, h = 0, widthspec, indx; t_glist *glist = x->x_glist; t_canvas *canvas = glist_getcanvas(glist); //if (state && x->x_active) printf("duplicate rtext_activate\n"); @@ -578,14 +585,24 @@ void rtext_activate(t_rtext *x, int state) glist->gl_editor->e_textedfor = 0; x->x_active = 0; } - /* so I guess the following would need to be commented out - if we want to use the textarea junk to feed to Pd... */ rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); - gui_vmess("gui_textarea", "xsiiii", + /* hack... + state = 0 no editing + state = 1 editing + state = 2 editing a new object + State 2 isn't necessary, except that Pd has + traditionally had this "floating" state for + new objects where the box text also happens + to be editable + */ + + widthspec = x->x_text->te_width; // width if any specified + gui_vmess("gui_textarea", "xsiiiii", canvas, x->x_tag, x->x_text->te_xpix, x->x_text->te_ypix, + widthspec, sys_hostfontsize(glist_getfont(glist)), state); } diff --git a/pd/src/g_text.c b/pd/src/g_text.c index 05de05aa4..1b845b50d 100644 --- a/pd/src/g_text.c +++ b/pd/src/g_text.c @@ -226,7 +226,8 @@ static void canvas_objtext(t_glist *gl, int xpix, int ypix, { /* this is called if we've been created from the menu. */ glist_select(gl, &x->te_g); - gobj_activate(&x->te_g, gl, 1); + gobj_activate(&x->te_g, gl, + 2); // <-- hack to signal that we're a new object } if (pd_class(&x->ob_pd) == vinlet_class) canvas_resortinlets(glist_getcanvas(gl)); @@ -357,7 +358,8 @@ void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv) pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); canvas_objtext(gl, connectme ? xpix : xpix - 8, - connectme ? ypix : ypix - 8, 0, 1, b); + connectme ? ypix : ypix - 8, + 0, 1, b); if (connectme == 1) { //fprintf(stderr,"canvas_obj calls canvas_connect\n"); -- GitLab