GUI -- Augmented pan/zoom & select/drag integration by having a toggle button fo…
…r whether meta needs to be pressed for panning (default) or selecting. - multi-select requires the shift key to be held down. - Also re-wired deselectAll() to the ESC key, instead of click on background. Change-Id: I63502839368c6ca10c64ee583a58f836576c4546
Showing
3 changed files
with
50 additions
and
33 deletions
... | @@ -37,6 +37,9 @@ | ... | @@ -37,6 +37,9 @@ |
37 | if (!$.isFunction(atDragEnd)) { | 37 | if (!$.isFunction(atDragEnd)) { |
38 | alert('d3util.createDragBehavior(): atDragEnd is not a function') | 38 | alert('d3util.createDragBehavior(): atDragEnd is not a function') |
39 | } | 39 | } |
40 | + if (!$.isFunction(requireMeta)) { | ||
41 | + alert('d3util.createDragBehavior(): requireMeta is not a function') | ||
42 | + } | ||
40 | 43 | ||
41 | function dragged(d) { | 44 | function dragged(d) { |
42 | var threshold = draggedThreshold(force.alpha()), | 45 | var threshold = draggedThreshold(force.alpha()), |
... | @@ -51,7 +54,7 @@ | ... | @@ -51,7 +54,7 @@ |
51 | drag = d3.behavior.drag() | 54 | drag = d3.behavior.drag() |
52 | .origin(function(d) { return d; }) | 55 | .origin(function(d) { return d; }) |
53 | .on('dragstart', function(d) { | 56 | .on('dragstart', function(d) { |
54 | - if (requireMeta ^ !d3.event.sourceEvent.metaKey) { | 57 | + if (requireMeta() ^ !d3.event.sourceEvent.metaKey) { |
55 | d3.event.sourceEvent.stopPropagation(); | 58 | d3.event.sourceEvent.stopPropagation(); |
56 | 59 | ||
57 | d.oldX = d.x; | 60 | d.oldX = d.x; |
... | @@ -62,7 +65,7 @@ | ... | @@ -62,7 +65,7 @@ |
62 | } | 65 | } |
63 | }) | 66 | }) |
64 | .on('drag', function(d) { | 67 | .on('drag', function(d) { |
65 | - if (requireMeta ^ !d3.event.sourceEvent.metaKey) { | 68 | + if (requireMeta() ^ !d3.event.sourceEvent.metaKey) { |
66 | d.px = d3.event.x; | 69 | d.px = d3.event.x; |
67 | d.py = d3.event.y; | 70 | d.py = d3.event.y; |
68 | if (dragged(d)) { | 71 | if (dragged(d)) { | ... | ... |
... | @@ -120,15 +120,16 @@ | ... | @@ -120,15 +120,16 @@ |
120 | 120 | ||
121 | // key bindings | 121 | // key bindings |
122 | var keyDispatch = { | 122 | var keyDispatch = { |
123 | - M: testMe, // TODO: remove (testing only) | 123 | + //M: testMe, // TODO: remove (testing only) |
124 | - S: injectStartupEvents, // TODO: remove (testing only) | 124 | + //S: injectStartupEvents, // TODO: remove (testing only) |
125 | - space: injectTestEvent, // TODO: remove (testing only) | 125 | + //space: injectTestEvent, // TODO: remove (testing only) |
126 | 126 | ||
127 | - B: toggleBg, // TODO: do we really need this? | 127 | + B: toggleBg, |
128 | L: cycleLabels, | 128 | L: cycleLabels, |
129 | P: togglePorts, | 129 | P: togglePorts, |
130 | U: unpin, | 130 | U: unpin, |
131 | R: resetZoomPan, | 131 | R: resetZoomPan, |
132 | + esc: deselectAll, | ||
132 | 133 | ||
133 | W: requestTraffic, // bag of selections | 134 | W: requestTraffic, // bag of selections |
134 | X: cancelTraffic, | 135 | X: cancelTraffic, |
... | @@ -1040,7 +1041,6 @@ | ... | @@ -1040,7 +1041,6 @@ |
1040 | node.append('circle') | 1041 | node.append('circle') |
1041 | .attr('r', 8); // TODO: define host circle radius | 1042 | .attr('r', 8); // TODO: define host circle radius |
1042 | 1043 | ||
1043 | - // TODO: are we attaching labels to hosts? | ||
1044 | node.append('text') | 1044 | node.append('text') |
1045 | .text(hostLabel) | 1045 | .text(hostLabel) |
1046 | .attr('dy', '1.3em') | 1046 | .attr('dy', '1.3em') |
... | @@ -1231,7 +1231,13 @@ | ... | @@ -1231,7 +1231,13 @@ |
1231 | 1231 | ||
1232 | function selectObject(obj, el) { | 1232 | function selectObject(obj, el) { |
1233 | var n, | 1233 | var n, |
1234 | - meta = d3.event.sourceEvent.metaKey; | 1234 | + srcEv = d3.event.sourceEvent, |
1235 | + meta = srcEv.metaKey, | ||
1236 | + shift = srcEv.shiftKey; | ||
1237 | + | ||
1238 | + if ((metaSelect() && !meta) || (!metaSelect() && meta)) { | ||
1239 | + return; | ||
1240 | + } | ||
1235 | 1241 | ||
1236 | if (el) { | 1242 | if (el) { |
1237 | n = d3.select(el); | 1243 | n = d3.select(el); |
... | @@ -1244,13 +1250,13 @@ | ... | @@ -1244,13 +1250,13 @@ |
1244 | } | 1250 | } |
1245 | if (!n) return; | 1251 | if (!n) return; |
1246 | 1252 | ||
1247 | - if (meta && n.classed('selected')) { | 1253 | + if (shift && n.classed('selected')) { |
1248 | deselectObject(obj.id); | 1254 | deselectObject(obj.id); |
1249 | updateDetailPane(); | 1255 | updateDetailPane(); |
1250 | return; | 1256 | return; |
1251 | } | 1257 | } |
1252 | 1258 | ||
1253 | - if (!meta) { | 1259 | + if (!shift) { |
1254 | deselectAll(); | 1260 | deselectAll(); |
1255 | } | 1261 | } |
1256 | 1262 | ||
... | @@ -1282,15 +1288,6 @@ | ... | @@ -1282,15 +1288,6 @@ |
1282 | updateDetailPane(); | 1288 | updateDetailPane(); |
1283 | } | 1289 | } |
1284 | 1290 | ||
1285 | - // FIXME: this click handler does not get unloaded when the view does | ||
1286 | - $('#view').on('click', function(e) { | ||
1287 | - if (!$(e.target).closest('.node').length) { | ||
1288 | - if (!e.metaKey) { | ||
1289 | - deselectAll(); | ||
1290 | - } | ||
1291 | - } | ||
1292 | - }); | ||
1293 | - | ||
1294 | // update the state of the detail pane, based on current selections | 1291 | // update the state of the detail pane, based on current selections |
1295 | function updateDetailPane() { | 1292 | function updateDetailPane() { |
1296 | var nSel = selectOrder.length; | 1293 | var nSel = selectOrder.length; |
... | @@ -1376,7 +1373,7 @@ | ... | @@ -1376,7 +1373,7 @@ |
1376 | 1373 | ||
1377 | function setupZoomPan() { | 1374 | function setupZoomPan() { |
1378 | function zoomed() { | 1375 | function zoomed() { |
1379 | - if (!d3.event.sourceEvent.metaKey) { | 1376 | + if (!metaSelect() ^ !d3.event.sourceEvent.metaKey) { |
1380 | zoomPan(d3.event.scale, d3.event.translate); | 1377 | zoomPan(d3.event.scale, d3.event.translate); |
1381 | } | 1378 | } |
1382 | } | 1379 | } |
... | @@ -1425,26 +1422,31 @@ | ... | @@ -1425,26 +1422,31 @@ |
1425 | 1422 | ||
1426 | } | 1423 | } |
1427 | 1424 | ||
1428 | - function para(sel, text) { | 1425 | + // ============================== |
1429 | - sel.append('p').text(text); | 1426 | + // Toggle Buttons in masthead |
1430 | - } | ||
1431 | 1427 | ||
1432 | // TODO: toggle button (and other widgets in the masthead) should be provided | 1428 | // TODO: toggle button (and other widgets in the masthead) should be provided |
1433 | // by the framework; not generated by the view. | 1429 | // by the framework; not generated by the view. |
1434 | 1430 | ||
1435 | - var showTrafficOnHover; | 1431 | + var showTrafficOnHover, |
1432 | + metaToSelect; | ||
1436 | 1433 | ||
1437 | function addButtonBar(view) { | 1434 | function addButtonBar(view) { |
1438 | var bb = d3.select('#mast') | 1435 | var bb = d3.select('#mast') |
1439 | .append('span').classed('right', true).attr('id', 'bb'); | 1436 | .append('span').classed('right', true).attr('id', 'bb'); |
1440 | 1437 | ||
1441 | - showTrafficOnHover = bb.append('div') | 1438 | + metaToSelect = bb.append('span') |
1439 | + .classed('btn', true) | ||
1440 | + .text('Meta to select') | ||
1441 | + .on('click', toggleMetaSelect); | ||
1442 | + | ||
1443 | + showTrafficOnHover = bb.append('span') | ||
1442 | .classed('btn', true) | 1444 | .classed('btn', true) |
1443 | .text('Show traffic on hover') | 1445 | .text('Show traffic on hover') |
1444 | - .on('click', toggleShowTraffic); | 1446 | + .on('click', toggleTrafficHover); |
1445 | } | 1447 | } |
1446 | 1448 | ||
1447 | - function toggleShowTraffic() { | 1449 | + function toggleTrafficHover() { |
1448 | showTrafficOnHover.classed('active', !trafficHover()); | 1450 | showTrafficOnHover.classed('active', !trafficHover()); |
1449 | } | 1451 | } |
1450 | 1452 | ||
... | @@ -1452,6 +1454,14 @@ | ... | @@ -1452,6 +1454,14 @@ |
1452 | return showTrafficOnHover.classed('active'); | 1454 | return showTrafficOnHover.classed('active'); |
1453 | } | 1455 | } |
1454 | 1456 | ||
1457 | + function toggleMetaSelect() { | ||
1458 | + metaToSelect.classed('active', !metaSelect()); | ||
1459 | + } | ||
1460 | + | ||
1461 | + function metaSelect() { | ||
1462 | + return metaToSelect.classed('active'); | ||
1463 | + } | ||
1464 | + | ||
1455 | // ============================== | 1465 | // ============================== |
1456 | // View life-cycle callbacks | 1466 | // View life-cycle callbacks |
1457 | 1467 | ||
... | @@ -1519,8 +1529,8 @@ | ... | @@ -1519,8 +1529,8 @@ |
1519 | id: d.id, | 1529 | id: d.id, |
1520 | 'class': d.class, | 1530 | 'class': d.class, |
1521 | 'memento': { | 1531 | 'memento': { |
1522 | - x: Math.floor(d.x), | 1532 | + x: d.x, |
1523 | - y: Math.floor(d.y) | 1533 | + y: d.y |
1524 | } | 1534 | } |
1525 | }); | 1535 | }); |
1526 | } | 1536 | } |
... | @@ -1537,7 +1547,8 @@ | ... | @@ -1537,7 +1547,8 @@ |
1537 | .linkStrength(lstrg) | 1547 | .linkStrength(lstrg) |
1538 | .on('tick', tick); | 1548 | .on('tick', tick); |
1539 | 1549 | ||
1540 | - network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd, true); // true=require meta | 1550 | + network.drag = d3u.createDragBehavior(network.force, |
1551 | + selectCb, atDragEnd, metaSelect); | ||
1541 | 1552 | ||
1542 | // create mask layer for when we lose connection to server. | 1553 | // create mask layer for when we lose connection to server. |
1543 | mask = view.$div.append('div').attr('id','topo-mask'); | 1554 | mask = view.$div.append('div').attr('id','topo-mask'); |
... | @@ -1546,6 +1557,11 @@ | ... | @@ -1546,6 +1557,11 @@ |
1546 | para(mask, 'Try refreshing the page.'); | 1557 | para(mask, 'Try refreshing the page.'); |
1547 | } | 1558 | } |
1548 | 1559 | ||
1560 | + function para(sel, text) { | ||
1561 | + sel.append('p').text(text); | ||
1562 | + } | ||
1563 | + | ||
1564 | + | ||
1549 | function load(view, ctx, flags) { | 1565 | function load(view, ctx, flags) { |
1550 | // resize, in case the window was resized while we were not loaded | 1566 | // resize, in case the window was resized while we were not loaded |
1551 | resize(view, ctx, flags); | 1567 | resize(view, ctx, flags); |
... | @@ -1647,9 +1663,6 @@ | ... | @@ -1647,9 +1663,6 @@ |
1647 | 1663 | ||
1648 | function resize(view, ctx, flags) { | 1664 | function resize(view, ctx, flags) { |
1649 | setSize(svg, view); | 1665 | setSize(svg, view); |
1650 | - | ||
1651 | - // TODO: hook to recompute layout, perhaps? work with zoom/pan code | ||
1652 | - // adjust force layout size | ||
1653 | } | 1666 | } |
1654 | 1667 | ||
1655 | 1668 | ... | ... |
-
Please register or login to post a comment