Commit 7cac18b9 authored by Jonathan Wilkes's avatar Jonathan Wilkes
Browse files

add constrained dragging to scale handles, resize cursors, and move handles

parent 9484aff7
Pipeline #1448 passed with stage
in 405 minutes and 43 seconds
......@@ -106,9 +106,8 @@ typedef struct _scope
int x_frozen;
t_clock *x_clock;
t_pd *x_handle;
int scale_offset_x;
int scale_offset_y;
int scale_offset_x;
int scale_offset_y;
} t_scope;
typedef struct _scopehandle
......@@ -118,6 +117,9 @@ typedef struct _scopehandle
t_symbol *h_bindsym;
char h_pathname[64];
char h_outlinetag[64];
int h_adjust_x;
int h_adjust_y;
int h_constrain;
int h_dragon;
int h_dragx;
int h_dragy;
......@@ -992,49 +994,16 @@ static void scope_tick(t_scope *x)
scope_clear(x, 1);
}
static void scopehandle__clickhook(t_scopehandle *sh, t_floatarg f, t_floatarg xxx, t_floatarg yyy)
extern void canvas_apply_setundo(t_canvas *x, t_gobj *y);
static void scopehandle__clickhook(t_scopehandle *sh, t_floatarg f,
t_floatarg xxx, t_floatarg yyy)
{
t_scope *x = sh->h_master;
//if (xxx) x->scale_offset_x = xxx;
//if (yyy) x->scale_offset_y = yyy;
//int newstate = (int)f;
//if (sh->h_dragon && newstate == 0)
//{
// /* done dragging */
// t_canvas *cv;
// if (sh->h_dragx || sh->h_dragy)
// {
// x->x_width = x->x_width + sh->h_dragx - x->scale_offset_x;
// x->x_height = x->x_height + sh->h_dragy - x->scale_offset_y;
// }
// if (cv = scope_isvisible(x))
// {
// sys_vgui(".x%x.c delete %s\n", cv, sh->h_outlinetag);
// scope_revis(x, cv);
// sys_vgui("destroy %s\n", sh->h_pathname);
// scope_select((t_gobj *)x, x->x_glist, 1);
// canvas_fixlinesfor(x->x_glist, (t_text *)x); /* 2nd inlet */
// }
//}
//else if (!sh->h_dragon && newstate)
//{
// /* dragging */
// t_canvas *cv;
// if (cv = scope_isvisible(x))
// {
// int x1, y1, x2, y2;
// scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2);
// sys_vgui("lower %s\n", sh->h_pathname);
// sys_vgui(".x%x.c create rectangle %d %d %d %d\
// -outline $select_color -width %f -tags %s\n",
// cv, x1, y1, x2, y2, SCOPE_SELBDWIDTH, sh->h_outlinetag);
// }
// sh->h_dragx = 0;
// sh->h_dragy = 0;
//}
/* Use constrained dragging. See g_canvas.c clickhook */
sh->h_constrain = (int)f;
sh->h_adjust_x = xxx - (((t_object *)x)->te_xpix + x->x_width);
sh->h_adjust_y = yyy - (((t_object *)x)->te_ypix + x->x_height);
canvas_apply_setundo(x->x_glist, (t_gobj *)x);
sh->h_dragon = f;
}
......@@ -1042,10 +1011,13 @@ static void scopehandle__motionhook(t_scopehandle *sh,
t_floatarg mouse_x, t_floatarg mouse_y)
{
t_scope *x = (t_scope *)(sh->h_master);
int x1, y1, x2, y2, width, height;
scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2);
width = mouse_x - x1;
height = mouse_y - y1;
int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
x->x_width :
(int)mouse_x - text_xpix((t_text *)x, x->x_glist) - sh->h_adjust_x;
int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
x->x_height :
(int)mouse_y - text_ypix((t_text *)x, x->x_glist) - sh->h_adjust_y;
x->x_width = width < SCOPE_MINWIDTH ? SCOPE_MINWIDTH : width;
x->x_height = height < SCOPE_MINHEIGHT ? SCOPE_MINHEIGHT : height;
......@@ -1058,25 +1030,6 @@ static void scopehandle__motionhook(t_scopehandle *sh,
scope_vis((t_gobj *)x, x->x_glist, 0);
scope_vis((t_gobj *)x, x->x_glist, 1);
}
//if (sh->h_dragon)
//{
// t_scope *x = sh->h_master;
// int dx = (int)f1, dy = (int)f2;
// int x1, y1, x2, y2, newx, newy;
// scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2);
// newx = x2 - x->scale_offset_x + dx;
// newy = y2 - x->scale_offset_y + dy;
// if (newx > x1 + SCOPE_MINWIDTH && newy > y1 + SCOPE_MINHEIGHT)
// {
// t_canvas *cv;
// if (cv = scope_isvisible(x))
// sys_vgui(".x%x.c coords %s %d %d %d %d\n",
// cv, sh->h_outlinetag, x1, y1, newx, newy);
// sh->h_dragx = dx;
// sh->h_dragy = dy;
// }
//}
}
/* wrapper method for forwarding "scopehandle" data */
......@@ -1157,8 +1110,8 @@ static void *scope_new(t_symbol *s, int ac, t_atom *av)
sprintf(sh->h_outlinetag, "h%x", (int)sh);
sh->h_dragon = 0;
x->scale_offset_x = 0;
x->scale_offset_y = 0;
x->scale_offset_x = 0;
x->scale_offset_y = 0;
return (x);
}
......
......@@ -696,39 +696,43 @@ static void grid_bang(t_grid *x) {
static void grid__clickhook(t_scalehandle *sh, int newstate)
{
t_grid *x = (t_grid *)(sh->h_master);
if (newstate)
{
canvas_apply_setundo(x->x_glist, (t_gobj *)x);
}
/* Use constrained dragging-- see g_canvas.c clickhook */
sh->h_constrain = newstate;
sh->h_adjust_x = sh->h_offset_x -
(((t_object *)x)->te_xpix + x->x_width);
sh->h_adjust_y = sh->h_offset_y -
(((t_object *)x)->te_ypix + x->x_height);
canvas_apply_setundo(x->x_glist, (t_gobj *)x);
sh->h_dragon = newstate;
}
static void grid__motionhook(t_scalehandle *sh,
t_floatarg mouse_x, t_floatarg mouse_y)
{
if (sh->h_scale)
t_grid *x = (t_grid *)(sh->h_master);
int width = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ?
x->x_width :
(int)mouse_x - text_xpix(&x->x_obj, x->x_glist) - sh->h_adjust_x;
int height = (sh->h_constrain == CURSOR_EDITMODE_RESIZE_X) ?
x->x_height :
(int)mouse_y - text_ypix(&x->x_obj, x->x_glist) - sh->h_adjust_y;
int minw = MIN_GRID_WIDTH,
minh = MIN_GRID_HEIGHT;
x->x_width = width < minw ? minw : width;
x->x_height = height < minh ? minh : height;
if (glist_isvisible(x->x_glist))
{
t_grid *x = (t_grid *)(sh->h_master);
int width = mouse_x - text_xpix(&x->x_obj, x->x_glist),
height = mouse_y - text_ypix(&x->x_obj, x->x_glist),
minw = MIN_GRID_WIDTH,
minh = MIN_GRID_HEIGHT;
x->x_width = width < minw ? minw : width;
x->x_height = height < minh ? minh : height;
if (glist_isvisible(x->x_glist))
{
grid_draw_configure(x, x->x_glist);
//scalehandle_unclick_scale(sh);
}
grid_draw_configure(x, x->x_glist);
//scalehandle_unclick_scale(sh);
}
int properties = gfxstub_haveproperties((void *)x);
if (properties)
{
int new_w = x->x_width + sh->h_dragx;
int new_h = x->x_height + sh->h_dragy;
properties_set_field_int(properties,"width",new_w);
properties_set_field_int(properties,"height",new_h);
}
int properties = gfxstub_haveproperties((void *)x);
if (properties)
{
int new_w = x->x_width + sh->h_dragx;
int new_h = x->x_height + sh->h_dragy;
properties_set_field_int(properties,"width",new_w);
properties_set_field_int(properties,"height",new_h);
}
}
......@@ -737,8 +741,9 @@ static void grid_click_for_resizing(t_grid *x, t_floatarg f,
t_floatarg xxx, t_floatarg yyy)
{
t_scalehandle *sh = (t_scalehandle *)x->x_handle;
sh->h_offset_x = (int)xxx;
sh->h_offset_y = (int)yyy;
grid__clickhook(sh, f);
// grid__clickhook(sh, f, xxx, yyy);
}
/* another wrapper for forwarding "scalehandle" motion data */
......
......@@ -233,25 +233,50 @@ var canvas_events = (function() {
return false;
},
mousedown: function(evt) {
var target_id;
var target_id, resize_type;
if (target_is_scrollbar(evt)) {
return;
} else if (evt.target.classList.contains("clickable_resize_handle")) {
} else if (evt.target.parentNode &&
evt.target.parentNode.classList
.contains("clickable_resize_handle")) {
draggable_label =
evt.target.classList.contains("move_handle");
evt.target.parentNode.classList.contains("move_handle");
// get id ("x123456etcgobj" without the "x" or "gobj")
target_id = (draggable_label ? "_l" : "_s") +
evt.target.parentNode.id.slice(0,-4).slice(1);
evt.target.parentNode.parentNode.id.slice(0,-4).slice(1);
last_draggable_x = evt.pageX + svg_view.x;
last_draggable_y = evt.pageY + svg_view.y;
pdgui.pdsend(target_id, "_click", 1,
// Nasty-- we have to forward magic values from g_canvas.h
// defines in order to get the correct constrain behavior.
if (evt.target.classList.contains("constrain_top_right")) {
resize_type = 7; // CURSOR_EDITMODE_RESIZE_X
} else if (evt.target.classList
.contains("constrain_bottom_right")) {
resize_type = 10; // CURSOR_EDITMODE_RESIZE_Y
} else if (draggable_label) {
resize_type = 11; // CURSOR_EDITMODE_MOVE
} else {
resize_type = 8; // CURSOR_EDITMODE_RESIZE
}
// Even nastier-- we now must turn off the cursor styles
// so that moving the pointer outside the hotspot doesn't
// cause the cursor to change. This happens for the
// drag handle to move the gop red rectangle. Unlike
// the label handles, it doesn't get immediately
// destroyed upon receiving its callback below.
pdgui.toggle_drag_handle_cursors(evt.target.parentNode,
!!draggable_label, false);
pdgui.pdsend(target_id, "_click", resize_type,
(evt.pageX + svg_view.x),
(evt.pageY + svg_view.y));
canvas_events.iemgui_label_drag();
return;
}
// tk events (and, therefore, Pd evnets) are one greater
// tk events (and, therefore, Pd events) are one greater
// than html5...
var b = evt.button + 1;
var mod, match_elem;
......@@ -472,16 +497,6 @@ var canvas_events = (function() {
last_draggable_x = evt.pageX + svg_view.x;
last_draggable_y = evt.pageY + svg_view.y;
if (!is_canvas_gop_rect) {
// This is bad-- we should be translating
// here so that the logic doesn't depend on the shape
// type we chose in pdgui (here, it's "line").
handle_elem.x1.baseVal.value += dx;
handle_elem.y1.baseVal.value += dy;
handle_elem.x2.baseVal.value += dx;
handle_elem.y2.baseVal.value += dy;
}
pdgui.pdsend(target_id, "_motion",
(evt.pageX + svg_view.x),
(evt.pageY + svg_view.y));
......@@ -491,6 +506,19 @@ var canvas_events = (function() {
// Set last state (none doesn't count as a state)
//pdgui.post("previous state is "
// + canvas_events.get_previous_state());
var label_handle = document.querySelector(".move_handle");
var cnv_resize_handle =
document.querySelector(".cnv_resize_handle");
// Restore our cursor bindings for any drag handles that
// happen to exist
if (label_handle) {
pdgui.toggle_drag_handle_cursors(label_handle,
true, true);
}
if (cnv_resize_handle) {
pdgui.toggle_drag_handle_cursors(cnv_resize_handle,
false, true);
}
canvas_events[canvas_events.get_previous_state()]();
},
dropdown_menu_keydown: function(evt) {
......
......@@ -1472,11 +1472,18 @@ function gui_canvas_cursor(cid, pd_event_type) {
case "cursor_editmode_resize":
c = "ew-resize";
break;
case "cursor_editmode_resize_bottom_right": c = "se-resize";
case "cursor_editmode_resize_bottom_right":
c = "se-resize";
break;
case "cursor_scroll":
c = "all-scroll";
break;
case "cursor_editmode_resize_vert":
c = "ns-resize";
break;
case "cursor_editmode_move":
c = "move";
break;
}
patch.style.cursor = c;
});
......@@ -2021,7 +2028,7 @@ var gui = (function() {
return {
append: !w ? null_fn: function(cb) {
var frag = w.window.document.createDocumentFragment();
frag = cb(frag, w.window);
frag = cb(frag, w.window, c[cid]);
last_thing.appendChild(frag);
return c[cid];
},
......@@ -3239,12 +3246,29 @@ function gui_iemgui_label_font(cid, tag, fontname, fontweight, fontsize) {
});
}
function toggle_drag_handle_cursors(e, is_label, state) {
e.querySelector(".constrain_top_right").style.cursor =
state ? "ew-resize" : "";
e.querySelector(".constrain_bottom_right").style.cursor =
state ? "ns-resize" : "";
e.querySelector(".unconstrained").style.cursor =
state ? (is_label ? "move" : "se-resize") : "";
}
exports.toggle_drag_handle_cursors = toggle_drag_handle_cursors;
// Show or hide little handle for dragging around iemgui labels
function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) {
if (state !== 0) {
gui(cid).get_gobj(tag)
.append(function(frag) {
var rect;
.append(function(frag, w) {
var g, rect, top_right, bottom_right;
g = create_item(cid, "g", {
class: (cid === tag) ? "gop_drag_handle move_handle border" :
cnv_resize !== 0 ? "cnv_resize_handle border" :
"label_drag_handle move_handle border",
transform: "matrix(1, 0, 0, 1, 0, 0)"
});
// Here we use a "line" shape so that we can control its color
// using the "border" class (for iemguis) or the "gop_rect" class
// for the graph-on-parent rectangle anchor. In both cases the
......@@ -3252,28 +3276,59 @@ function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) {
// to define than a "rect" for that case.
rect = create_item(cid, "line", {
x1: x,
y1: y + 3,
y1: y,
x2: x,
y2: y + 10,
"stroke-width": 7,
class: (cid === tag) ? "gop_drag_handle move_handle gop_rect" :
cnv_resize !== 0 ? "cnv_resize_handle border" :
"label_drag_handle move_handle border"
y2: y + 14,
"stroke-width": 14,
class: "unconstrained"
});
g.classList.add("clickable_resize_handle");
top_right = create_item(cid, "rect", {
x: x + 1.5,
y: y + 0.5,
width: 5,
height: 7,
fill: "black",
"fill-opacity": "0",
class: "constrain_top_right"
});
rect.classList.add("clickable_resize_handle");
frag.appendChild(rect);
bottom_right = create_item(cid, "rect", {
x: x - 6.5,
y: y + 8.5,
width: 7,
height: 5,
fill: "black",
"fill-opacity": "0",
class: "constrain_bottom_right"
});
g.appendChild(rect);
g.appendChild(top_right);
g.appendChild(bottom_right);
// Quick hack for cursors on mouse-over. We only add them if
// we're not already dragging a label or resizing an iemgui.
// Apparently I didn't register all these edge-case event states
// in canvas_events. States like "iemgui_label_drag" actually
// just get registered as state "none". So we just check for "none"
// here and assume it means we're in the middle of dragging.
// If not we go ahead and set our cursor styles.
if (w.canvas_events.get_state() != "none") {
toggle_drag_handle_cursors(g, cnv_resize === 0, true);
}
frag.appendChild(g);
return frag;
});
} else {
gui(cid).get_gobj(tag, function(e) {
var rect =
var g =
e.getElementsByClassName((cid === tag) ? "gop_drag_handle" :
cnv_resize !== 0 ? "cnv_resize_handle" :
"label_drag_handle")[0];
//rect = get_item(cid, "clickable_resize_handle");
// Need to check for null here...
if (rect) {
rect.parentNode.removeChild(rect);
if (g) {
g.parentNode.removeChild(g);
} else {
post("error: couldn't delete the iemgui drag handle!");
}
......@@ -3281,6 +3336,15 @@ function gui_iemgui_label_show_drag_handle(cid, tag, state, x, y, cnv_resize) {
}
}
function gui_iemgui_label_displace_drag_handle(cid, tag, dx, dy) {
gui(cid).get_gobj(tag)
.q(".label_drag_handle", function(e) {
var t = e.transform.baseVal.getItem(0);
t.matrix.e += dx;
t.matrix.f += dy;
});
}
function gui_mycanvas_new(cid,tag,color,x1,y1,x2_vis,y2_vis,x2,y2) {
gui(cid).get_gobj(tag)
.append(function(frag) {
......
......@@ -246,7 +246,7 @@ void iemgui_label_pos(t_iemgui *x, t_symbol *s, int ac, t_atom *av)
{
x->x_ldx = atom_getintarg(0, ac, av);
x->x_ldy = atom_getintarg(1, ac, av);
if(glist_isvisible(x->x_glist))
if (glist_isvisible(x->x_glist))
{
int x1 = x->x_ldx;
int y1 = x->x_ldy;
......@@ -474,7 +474,7 @@ void iemgui_pos(t_iemgui *x, t_symbol *s, int ac, t_atom *av)
{
x->x_obj.te_xpix = atom_getintarg(0, ac, av);
x->x_obj.te_ypix = atom_getintarg(1, ac, av);
if(glist_isvisible(x->x_glist))
if (glist_isvisible(x->x_glist))
iemgui_shouldvis(x, IEM_GUI_DRAW_MODE_MOVE);
}
......@@ -488,7 +488,7 @@ void iemgui_color(t_iemgui *x, t_symbol *s, int ac, t_atom *av)
}
else
x->x_lcol = iemgui_compatible_col(atom_getintarg(1, ac, av));
if(glist_isvisible(x->x_glist))
if (glist_isvisible(x->x_glist))
{
x->x_draw(x, x->x_glist, IEM_GUI_DRAW_MODE_CONFIG);
iemgui_label_draw_config(x);
......@@ -713,7 +713,7 @@ void scalehandle_draw_select2(t_iemgui *x)
{
t_canvas *canvas=glist_getcanvas(x->x_glist);
t_class *c = pd_class((t_pd *)x);
int sx,sy;
int sx, sy;
if (c == my_canvas_class)
{
t_my_canvas *y = (t_my_canvas *)x;
......@@ -722,24 +722,27 @@ void scalehandle_draw_select2(t_iemgui *x)
}
else
{
int x1,y1,x2,y2;
c->c_wb->w_getrectfn((t_gobj *)x,canvas,&x1,&y1,&x2,&y2);
int x1, y1, x2, y2;
c->c_wb->w_getrectfn((t_gobj *)x,canvas, &x1, &y1, &x2, &y2);
//iemgui_getrect_draw(x, &x1, &y1, &x2, &y2);
sx=x2-x1; sy=y2-y1;
sx = x2 - x1; sy = y2 - y1;
}
/* we're not drawing the scalehandle for the actual iemgui-- just
the one for the label. */
the one for the label. Special case for [cnv] which for some reason
allows a smaller selection area than its painted rectangle. */
if (c == my_canvas_class)
scalehandle_draw_select(x->x_handle,sx+8,sy+3);
scalehandle_draw_select(x->x_handle,
(int)(sx + SCALEHANDLE_WIDTH * 1.5) + 1,
sy + SCALEHANDLE_HEIGHT);
if (x->x_lab != s_empty)
scalehandle_draw_select(x->x_lhandle,x->x_ldx,x->x_ldy);
scalehandle_draw_select(x->x_lhandle, x->x_ldx + 5, x->x_ldy + 10);
}
void scalehandle_draw_erase(t_scalehandle *h)
{
//t_canvas *canvas = glist_getcanvas(h->h_glist);
if (!h->h_vis) return;
gui_vmess("gui_iemgui_label_show_drag_handle", "xxiiii",
gui_vmess("gui_iemgui_label_show_drag_handle", "xxiiiii",
h->h_glist, h->h_master, 0, 0, 0, h->h_scale);
h->h_vis = 0;
}
......@@ -783,7 +786,10 @@ t_scalehandle *scalehandle_new(t_object *x, t_glist *glist, int scale,
h->h_scale = scale;
h->h_offset_x = 0;
h->h_offset_y = 0;
h->h_adjust_x = 0;
h->h_adjust_y = 0;
h->h_vis = 0;
h->h_constrain = 0;
sprintf(h->h_pathname, ".x%lx.h%lx", (t_int)h->h_glist, (t_int)h);
h->h_clickfn = chf;
h->h_motionfn = mhf;
......@@ -814,8 +820,10 @@ void scalehandle_dragon_label(t_scalehandle *h, float mouse_x, float mouse_y)
if (h->h_dragon && !h->h_scale)
{
t_iemgui *x = (t_iemgui *)(h->h_master);
int dx = (int)mouse_x - (int)h->h_offset_x,
dy = (int)mouse_y - (int)h->h_offset_y;
int dx = (h->h_constrain == CURSOR_EDITMODE_RESIZE_Y) ? 0 :
(int)mouse_x - (int)h->h_offset_x,
dy = (h->h_constrain == CURSOR_EDITMODE_RESIZE_X) ? 0 :
(int)mouse_y - (int)h->h_offset_y;
h->h_dragx = dx;
h->h_dragy = dy;
......@@ -841,6 +849,11 @@ void scalehandle_dragon_label(t_scalehandle *h, float mouse_x, float mouse_y)
x,
x->x_ldx,
x->x_ldy);
gui_vmess("gui_iemgui_label_displace_drag_handle", "xxii",
canvas,
x,
dx,
dy);
}
}
}
......@@ -914,15 +927,25 @@ void scalehandle_drag_scale(t_scalehandle *h) {
static void scalehandle_clickhook(t_scalehandle *h, t_floatarg f,
t_floatarg xxx, t_floatarg yyy)
{
h->h_offset_x=xxx;
h->h_offset_y=yyy;
h->h_clickfn(h,f);
/* The label scalehandles do an end run around canvas_doclick,
which is where the cursor gets set. So we go ahead and set
the cursor here, too. "f" is our cursor number as defined
by the CURSOR_* macros in canvas.h */
canvas_setcursor(h->h_glist, (int)f);
/* We also go ahead and set h_constrain here so we don't have
to do it separately for each widget. Any widget that wants
to constrain movement along an axis can just check that
field against the CURSOR_* values in the motionhook callback. */
h->h_constrain = f;
h->h_offset_x = xxx;
h->h_offset_y = yyy;
h->h_clickfn(h, f);
}
static void scalehandle_motionhook(t_scalehandle *h,
t_floatarg f1, t_floatarg f2)
{
h->h_motionfn(h,f1,f2);
h->h_motionfn(h, f1, f2);
// Now set the offset to the new mouse position
h->h_offset_x = f1;
h->h_offset_y = f2;
......@@ -948,7 +971,8 @@ static void scalehandle_check_and_redraw(t_iemgui *x)
//----------------------------------------------------------------
// IEMGUI refactor (by Mathieu)
void iemgui_tag_selected(t_iemgui *x) {
void iemgui_tag_selected(t_iemgui *x)
{
t_canvas *canvas=glist_getcanvas(x->x_glist);
if (x->x_selected)
gui_vmess("gui_gobj_select", "xx", canvas, x);
......@@ -956,7 +980,8 @@ void iemgui_tag_selected(t_iemgui *x) {
gui_vmess("gui_gobj_deselect", "xx", canvas, x);
}
void iemgui_label_draw_new(t_iemgui *x) {
void iemgui_label_draw_new(t_iemgui *x)
{
char col[8];
t_canvas *canvas=glist_getcanvas(x->x_glist);
int x1=text_xpix(&x->x_obj, x->x_glist)+x->legacy_x;
......@@ -994,7 +1019,8 @@ void iemgui_label_draw_move(t_iemgui *x)
x->x_ldy + x->legacy_y);
}
void iemgui_label_draw_config(t_iemgui *x) {