Commit 6dc3d006 authored by Jonathan Wilkes's avatar Jonathan Wilkes
Browse files

allow different grid sizes (factors of 100 for now, but easily changed)

We also keep a partially transparent version of a grid for editmode. This
allows us to keep editmode visually distinct from runmode, even if snap-to-grid
isn't turned on.

If needed it's pretty easy to make the following changes:

* set the "big" cell size to something that isn't 100 (but I'm not sure how
  much control over the grid we actually want to give users-- e.g., setting
  the big cell to something for which the small cell isn't a factor
* allow snap-to-grid to be set independently for each canvas. (But again,
  what would be the use case for this?
parent f51a52fb
......@@ -415,9 +415,16 @@ select {
</select>
<br/>
<label data-i18n="[title]prefs.gui.grid.show_grid_tt">
<input type="checkbox" id="show_grid" name="show_grid">
<input type="checkbox" id="show_grid" name="show_grid"
onchange="grid_toggle(this.checked)">
<span data-i18n="prefs.gui.grid.show_grid"></span>
</label>
<select id="grid_size">
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
<option value="25">25</option>
</select>
<br/>
<label data-i18n="[title]prefs.gui.zoom.save_zoom_tt">
<input type="checkbox" id="save_zoom" name="save_zoom">
......@@ -783,6 +790,7 @@ function apply(save_prefs) {
pdgui.pdsend("pd gui-prefs",
get_gui_preset(),
get_bool_elem("show_grid"),
document.getElementById("grid_size").value,
get_bool_elem("save_zoom"),
get_bool_elem("browser_doc"),
get_bool_elem("browser_path"),
......@@ -796,7 +804,8 @@ function apply(save_prefs) {
get_bool_elem("browser_path")
);
// Update the grid on all open windows.
pdgui.update_grid(get_bool_elem("show_grid"));
pdgui.update_grid(get_bool_elem("show_grid"),
document.getElementById("grid_size").value);
// Send the startup config data to Pd
pdgui.pdsend.apply(null, ["pd path-dialog", startup_use_stdpath, startup_verbose].concat(get_path_array()));
......@@ -1035,13 +1044,18 @@ function midi_prefs_callback(attrs) {
pdgui.resize_window(pd_object_callback);
}
function grid_toggle(checked) {
document.getElementById("grid_size").disabled = !checked;
document.getElementById("show_grid").checked = checked;
}
function autopatch_yoffset_toggle(checked) {
document.getElementById("autopatch_yoffset_value").disabled = !checked;
document.getElementById("autopatch_yoffset").checked = checked;
}
function gui_prefs_callback(name, show_grid, save_zoom, browser_doc, browser_path,
browser_init, autopatch_yoffset) {
function gui_prefs_callback(name, show_grid, grid_size, save_zoom, browser_doc,
browser_path, browser_init, autopatch_yoffset) {
var s = document.getElementById("gui_preset");
// ag: scan the css subdir for user-defined styles
......@@ -1071,6 +1085,8 @@ function gui_prefs_callback(name, show_grid, save_zoom, browser_doc, browser_pat
}
}
document.getElementById("show_grid").checked = !!show_grid;
document.getElementById("grid_size").value = grid_size;
grid_toggle(!!show_grid);
document.getElementById("save_zoom").checked = !!save_zoom;
document.getElementById("browser_doc").checked = !!browser_doc;
document.getElementById("browser_path").checked = !!browser_path;
......
......@@ -1405,36 +1405,41 @@ function menu_send(name) {
}
}
/*
ico@vt.edu 20200907: added svg tiled background to reflect edit mode and
integrated it into the canvas_set_editmode below.
LATER: consider adding an interim version that reflects only the ctrl button press
*/
var gui_editmode_svg_background = "url(\"data:image/svg+xml,%3Csvg " +
"xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 " +
" 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%239C92AC' fill-opacity" +
"='0.4'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1" +
"v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-" +
"1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9" +
"H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9" +
"V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v" +
"1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h" +
"9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-1" +
"0 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9" +
"h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-" +
"10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9" +
"h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9" +
"v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h" +
"9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h" +
"9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-1" +
"0 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-" +
"9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm1" +
"0 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9" +
"h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9" +
"h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0" +
"h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d" +
"='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\")";
// Background for edit mode. Currently, we use a grid if snap-to-grid
// functionality is turned on in the GUI preferences. If not, we just use
// the same grid with a lower opacity. That way the edit mode is always
// visually distinct from run mode.
var create_editmode_background = function(grid, size) {
var head, body, tail, cell_data_str, opacity_str;
// if snap-to-grid isn't turned on, just use cell size of 10 and make the
// grid partially transparent
size = grid ? size : 10;
opacity_str = '"' + (grid ? 1 : 0.25) + '"';
post("size is " + size);
cell_data_str = ['"', "M", size, 0, "L", 0, 0, 0, size, '"'].join(" ");
head = ['<svg xmlns="http://www.w3.org/2000/svg" ',
'width="100%" height="100%" ',
'opacity=', opacity_str, '>']
.join("");
body = ['<defs>',
'<pattern id="cell" patternUnits="userSpaceOnUse" ',
'width="', size, '" height="', size, '">',
'<path fill="none" stroke="#ddd" stroke-width="1" ',
'd=', cell_data_str,'/>',
'</pattern>',
'<pattern id="grid" patternUnits="userSpaceOnUse" ',
'width="100" height="100">',
'<rect width="100" height="100" fill="url(#cell)" />',
'<path fill="none" stroke="#bbb" stroke-width="1" ',
'd="M 100 0 L 0 0 0 100"/>',
'</pattern>',
'</defs>',
'<rect width="100%" height="100%" fill="url(#grid)" />'
].join("");
tail = '</svg>';
return "url('data:image/svg+xml;utf8," + head + body + tail + "')";
}
// requires nw.js API (Menuitem)
function canvas_set_editmode(cid, state) {
......@@ -1442,15 +1447,17 @@ function canvas_set_editmode(cid, state) {
w.set_editmode_checkbox(state !== 0 ? true : false);
if (state !== 0) {
patchsvg.classList.add("editmode");
if (showgrid[cid]) {
//post("editmode:" + gui_editmode_svg_background);
patchwin[cid].window.document.body.style.setProperty
("background-image", gui_editmode_svg_background);
}
// For now, we just change the opacity of the background grid
// depending on whether snap-to-grid is turned on. This way
// edit mode is always visually distinct.
patchwin[cid].window.document.body.style
.setProperty("background-image",
create_editmode_background(showgrid[cid], gridsize[cid]));
} else {
patchsvg.classList.remove("editmode");
patchwin[cid].window.document.body.style.setProperty("background-image",
"none");
patchwin[cid].window.document.body.style
.setProperty("background-image", "none"
);
}
});
}
......@@ -1468,21 +1475,21 @@ function canvas_query_editmode(cid) {
exports.canvas_query_editmode = canvas_query_editmode;
function update_grid(grid) {
function update_grid(grid, grid_size_value) {
// Update the grid background of all canvas windows when the corresponding
// option in the gui prefs changes.
var bg = grid != 0 ? gui_editmode_svg_background : "none";
for (var cid in patchwin) {
gui(cid).get_elem("patchsvg", function(patchsvg, w) {
var editmode = patchsvg.classList.contains("editmode");
if (editmode) {
patchwin[cid].window.document.body.style.setProperty
("background-image", bg);
("background-image", create_editmode_background(grid !== 0,
grid_size_value));
}
});
}
// Also update the showgrid flags.
set_showgrid(grid);
set_grid(grid, grid_size_value);
}
exports.update_grid = update_grid;
......@@ -1744,6 +1751,7 @@ var scroll = {},
font = {},
doscroll = {},
showgrid = {},
gridsize = {},
last_loaded, // last loaded canvas
last_focused, // last focused canvas (doesn't include Pd window or dialogs)
loading = {},
......@@ -1754,9 +1762,11 @@ var scroll = {},
var patchwin = {}; // object filled with cid: [Window object] pairs
var dialogwin = {}; // object filled with did: [Window object] pairs
var set_showgrid = function(grid) {
for (var cid in showgrid) {
var set_grid = function(grid, gridsize_value) {
var cid;
for (cid in showgrid) {
showgrid[cid] = grid;
gridsize[cid] = gridsize_value;
}
}
......@@ -1919,7 +1929,9 @@ function create_window(cid, type, width, height, xpos, ypos, attr_array) {
}
// create a new canvas
function gui_canvas_new(cid, width, height, geometry, grid, zoom, editmode, name, dir, dirty_flag, warid, hide_scroll, hide_menu, has_toplevel_scalars, cargs) {
function gui_canvas_new(cid, width, height, geometry, grid, grid_size_value,
zoom, editmode, name, dir, dirty_flag, warid, hide_scroll, hide_menu,
has_toplevel_scalars, cargs) {
// hack for buggy tcl popups... should go away for node-webkit
//reset_ctrl_on_popup_window
......@@ -1947,6 +1959,7 @@ function gui_canvas_new(cid, width, height, geometry, grid, zoom, editmode, name
font[cid] = 10;
doscroll[cid] = 0;
showgrid[cid] = grid != 0;
gridsize[cid] = grid_size_value;
toplevel_scalars[cid] = has_toplevel_scalars;
// geometry is just the x/y screen offset "+xoff+yoff"
geometry = geometry.slice(1); // remove the leading "+"
......@@ -6190,10 +6203,10 @@ function gui_midi_properties(gfxstub, sys_indevs, sys_outdevs,
}
}
function gui_gui_properties(dummy, name, show_grid, save_zoom, browser_doc, browser_path,
function gui_gui_properties(dummy, name, show_grid, grid_size, save_zoom, browser_doc, browser_path,
browser_init, autopatch_yoffset) {
if (dialogwin["prefs"] !== null) {
dialogwin["prefs"].window.gui_prefs_callback(name, show_grid, save_zoom,
dialogwin["prefs"].window.gui_prefs_callback(name, show_grid, grid_size, save_zoom,
browser_doc, browser_path, browser_init, autopatch_yoffset);
}
}
......
......@@ -2799,6 +2799,9 @@ void canvas_init_menu(t_canvas *x)
gui_vmess("gui_menu_font_set_initial_size", "xi", x, x->gl_font);
}
extern int sys_snaptogrid; /* whether we are snapping to grid or not */
extern int sys_gridsize;
void canvas_vis(t_canvas *x, t_floatarg f)
{
//fprintf(stderr,"canvas_vis .x%zx %f\n", (t_int)x, f);
......@@ -2809,7 +2812,6 @@ void canvas_vis(t_canvas *x, t_floatarg f)
t_gobj *g;
t_int properties;
extern int sys_grid;
int flag = (f != 0);
if (flag)
......@@ -2865,12 +2867,13 @@ void canvas_vis(t_canvas *x, t_floatarg f)
We may need to expand this to include scalars, as well. */
canvas_create_editor(x);
canvas_args_to_string(argsbuf, x);
gui_vmess("gui_canvas_new", "xiisiiissiiiiis",
gui_vmess("gui_canvas_new", "xiisiiiissiiiiis",
x,
(int)(x->gl_screenx2 - x->gl_screenx1),
(int)(x->gl_screeny2 - x->gl_screeny1),
geobuf,
sys_grid,
sys_snaptogrid,
sys_gridsize,
x->gl_zoom,
x->gl_edit,
x->gl_name->s_name,
......@@ -3637,6 +3640,8 @@ static int canvas_upx, canvas_upy;
static int ctrl_runmode_warned;
static int snap_got_anchor;
/* mouse click */
void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
int mod, int doit)
......@@ -3646,6 +3651,10 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
to array_motion so that we can update corresponding send when
the array has been changed */
array_garray = NULL;
/* reset the snap_got_anchor variable so the the snap_to_grid feature
can find its anchor object before it starts to displace a selection */
snap_got_anchor = 0;
//post("canvas_doclick %d", doit);
t_gobj *y;
......@@ -5767,15 +5776,78 @@ void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
extern void graph_checkgop_rect(t_gobj *z, t_glist *glist,
int *xp1, int *yp1, int *xp2, int *yp2);
/* We get the bbox for the object under the mouse the first time around,
then cache its offset from the mouse for future motion messages.
Since there are cases where snapping to a grid can move the object
relative to the mouse pointer, we can't rely on our "anchor" object to
always be directly under the mouse coordinates. */
static void snap_get_anchor_xy(t_canvas *x, int *gobj_x, int *gobj_y)
{
t_selection *s = x->gl_editor->e_selection;
int x1, y1, x2, y2;
while (s)
{
if (canvas_hitbox(x, s->sel_what, x->gl_editor->e_xwas,
x->gl_editor->e_ywas, &x1, &y1, &x2, &y2))
{
*gobj_x = x1;
*gobj_y = y1;
return;
}
s = s->sel_next;
}
bug("canvas_get_snap_offset");
}
int anchor_xoff;
int anchor_yoff;
static void canvas_snap_to_grid(t_canvas *x, int xwas, int ywas, int xnew,
int ynew, int *dx, int *dy)
{
int gsize = sys_gridsize;
/* If we're snapping to grid, we need an initial delta to align
the object under the mouse to the given gridlines. We keep
that in the variables below, which will have no affect after
our initial grid adjustment. */
int snap_dx = 0, snap_dy = 0;
if (!snap_got_anchor)
{
int obx = xnew, oby = ynew;
snap_get_anchor_xy(x, &obx, &oby);
/* First, get the distance the selection should be displaced
in order to align the anchor object with a grid line. */
snap_dx = floor((obx + (gsize / 2)) / gsize) * gsize - obx;
snap_dy = floor((oby + (gsize / 2)) / gsize) * gsize - oby;
obx = floor(obx / gsize) * gsize;
oby = floor(oby / gsize) * gsize;
anchor_xoff = xnew - obx;
anchor_yoff = ynew - oby;
snap_got_anchor = 1;
}
*dx = floor((xnew - anchor_xoff) / gsize) * gsize -
floor((xwas - anchor_xoff) / gsize) * gsize + snap_dx;
*dy = floor((ynew - anchor_yoff) / gsize) * gsize -
floor((ywas - anchor_yoff) / gsize) * gsize + snap_dy;
}
static void delay_move(t_canvas *x)
{
canvas_displaceselection(x,
x->gl_editor->e_xnew - x->gl_editor->e_xwas,
x->gl_editor->e_ynew - x->gl_editor->e_ywas);
x->gl_editor->e_xwas = x->gl_editor->e_xnew;
x->gl_editor->e_ywas = x->gl_editor->e_ynew;
int dx, dy;
int xwas = x->gl_editor->e_xwas, ywas = x->gl_editor->e_ywas,
xnew = x->gl_editor->e_xnew, ynew = x->gl_editor->e_ynew;
if (sys_snaptogrid)
canvas_snap_to_grid(x, xwas, ywas, xnew, ynew, &dx, &dy);
else
{
dx = xnew - xwas;
dy = ynew - ywas;
}
canvas_displaceselection(x, dx, dy);
x->gl_editor->e_xwas = xnew;
x->gl_editor->e_ywas = ynew;
}
void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
......
......@@ -79,13 +79,14 @@ static void glob_perf(t_pd *dummy, float f)
sys_perf = (f != 0);
}
extern int sys_grid, sys_zoom, sys_browser_doc, sys_browser_path, sys_browser_init,
extern int sys_snaptogrid, sys_gridsize, sys_zoom, sys_browser_doc, sys_browser_path, sys_browser_init,
sys_autopatch_yoffset;
extern t_symbol *sys_gui_preset;
static void glob_gui_prefs(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)
{
sys_gui_preset = atom_getsymbolarg(0, argc--, argv++);
sys_grid = !!atom_getintarg(0, argc--, argv++);
sys_snaptogrid = !!atom_getintarg(0, argc--, argv++);
sys_gridsize = atom_getintarg(0, argc--, argv++);
sys_zoom = !!atom_getintarg(0, argc--, argv++);
sys_browser_doc = !!atom_getintarg(0, argc--, argv++);
sys_browser_path = !!atom_getintarg(0, argc--, argv++);
......@@ -96,10 +97,11 @@ static void glob_gui_prefs(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)
/* just the gui-preset, the save-zoom toggle and various help browser options for now */
static void glob_gui_properties(t_pd *dummy)
{
gui_vmess("gui_gui_properties", "xsiiiiii",
gui_vmess("gui_gui_properties", "xsiiiiiii",
dummy,
sys_gui_preset->s_name,
sys_grid,
sys_snaptogrid,
sys_gridsize,
sys_zoom,
sys_browser_doc,
sys_browser_path,
......
......@@ -42,7 +42,7 @@
#define snprintf sprintf_s
#endif
int sys_defeatrt, sys_autopatch_yoffset, sys_grid = 1, sys_zoom, sys_browser_doc = 1,
int sys_defeatrt, sys_autopatch_yoffset, sys_snaptogrid = 1, sys_gridsize = 10, sys_zoom, sys_browser_doc = 1,
sys_browser_path, sys_browser_init;
t_symbol *sys_flags = &s_;
void sys_doflags( void);
......@@ -671,7 +671,9 @@ void sys_loadpreferences( void)
if (sys_getpreference("defeatrt", prefbuf, MAXPDSTRING))
sscanf(prefbuf, "%d", &sys_defeatrt);
if (sys_getpreference("showgrid", prefbuf, MAXPDSTRING))
sscanf(prefbuf, "%d", &sys_grid);
sscanf(prefbuf, "%d", &sys_snaptogrid);
if (sys_getpreference("gridsize", prefbuf, MAXPDSTRING))
sscanf(prefbuf, "%d", &sys_gridsize);
if (sys_getpreference("savezoom", prefbuf, MAXPDSTRING))
sscanf(prefbuf, "%d", &sys_zoom);
if (sys_getpreference("browser_doc", prefbuf, MAXPDSTRING))
......@@ -819,8 +821,10 @@ void glob_savepreferences(t_pd *dummy)
sys_putpreference("nloadlib", buf1);
sprintf(buf1, "%d", sys_defeatrt);
sys_putpreference("defeatrt", buf1);
sprintf(buf1, "%d", sys_grid);
sprintf(buf1, "%d", sys_snaptogrid);
sys_putpreference("showgrid", buf1);
sprintf(buf1, "%d", sys_gridsize);
sys_putpreference("gridsize", buf1);
sprintf(buf1, "%d", sys_zoom);
sys_putpreference("savezoom", buf1);
sprintf(buf1, "%d", sys_browser_doc);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment