Simon Hunt

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
......@@ -37,6 +37,9 @@
if (!$.isFunction(atDragEnd)) {
alert('d3util.createDragBehavior(): atDragEnd is not a function')
}
if (!$.isFunction(requireMeta)) {
alert('d3util.createDragBehavior(): requireMeta is not a function')
}
function dragged(d) {
var threshold = draggedThreshold(force.alpha()),
......@@ -51,7 +54,7 @@
drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on('dragstart', function(d) {
if (requireMeta ^ !d3.event.sourceEvent.metaKey) {
if (requireMeta() ^ !d3.event.sourceEvent.metaKey) {
d3.event.sourceEvent.stopPropagation();
d.oldX = d.x;
......@@ -62,7 +65,7 @@
}
})
.on('drag', function(d) {
if (requireMeta ^ !d3.event.sourceEvent.metaKey) {
if (requireMeta() ^ !d3.event.sourceEvent.metaKey) {
d.px = d3.event.x;
d.py = d3.event.y;
if (dragged(d)) {
......
......@@ -100,6 +100,7 @@
}
#bb .btn {
margin: 0 4px;
padding: 2px 6px;
font-size: 9pt;
cursor: pointer;
......
......@@ -120,15 +120,16 @@
// key bindings
var keyDispatch = {
M: testMe, // TODO: remove (testing only)
S: injectStartupEvents, // TODO: remove (testing only)
space: injectTestEvent, // TODO: remove (testing only)
//M: testMe, // TODO: remove (testing only)
//S: injectStartupEvents, // TODO: remove (testing only)
//space: injectTestEvent, // TODO: remove (testing only)
B: toggleBg, // TODO: do we really need this?
B: toggleBg,
L: cycleLabels,
P: togglePorts,
U: unpin,
R: resetZoomPan,
esc: deselectAll,
W: requestTraffic, // bag of selections
X: cancelTraffic,
......@@ -1040,7 +1041,6 @@
node.append('circle')
.attr('r', 8); // TODO: define host circle radius
// TODO: are we attaching labels to hosts?
node.append('text')
.text(hostLabel)
.attr('dy', '1.3em')
......@@ -1231,7 +1231,13 @@
function selectObject(obj, el) {
var n,
meta = d3.event.sourceEvent.metaKey;
srcEv = d3.event.sourceEvent,
meta = srcEv.metaKey,
shift = srcEv.shiftKey;
if ((metaSelect() && !meta) || (!metaSelect() && meta)) {
return;
}
if (el) {
n = d3.select(el);
......@@ -1244,13 +1250,13 @@
}
if (!n) return;
if (meta && n.classed('selected')) {
if (shift && n.classed('selected')) {
deselectObject(obj.id);
updateDetailPane();
return;
}
if (!meta) {
if (!shift) {
deselectAll();
}
......@@ -1282,15 +1288,6 @@
updateDetailPane();
}
// FIXME: this click handler does not get unloaded when the view does
$('#view').on('click', function(e) {
if (!$(e.target).closest('.node').length) {
if (!e.metaKey) {
deselectAll();
}
}
});
// update the state of the detail pane, based on current selections
function updateDetailPane() {
var nSel = selectOrder.length;
......@@ -1376,7 +1373,7 @@
function setupZoomPan() {
function zoomed() {
if (!d3.event.sourceEvent.metaKey) {
if (!metaSelect() ^ !d3.event.sourceEvent.metaKey) {
zoomPan(d3.event.scale, d3.event.translate);
}
}
......@@ -1425,26 +1422,31 @@
}
function para(sel, text) {
sel.append('p').text(text);
}
// ==============================
// Toggle Buttons in masthead
// TODO: toggle button (and other widgets in the masthead) should be provided
// by the framework; not generated by the view.
var showTrafficOnHover;
var showTrafficOnHover,
metaToSelect;
function addButtonBar(view) {
var bb = d3.select('#mast')
.append('span').classed('right', true).attr('id', 'bb');
showTrafficOnHover = bb.append('div')
metaToSelect = bb.append('span')
.classed('btn', true)
.text('Meta to select')
.on('click', toggleMetaSelect);
showTrafficOnHover = bb.append('span')
.classed('btn', true)
.text('Show traffic on hover')
.on('click', toggleShowTraffic);
.on('click', toggleTrafficHover);
}
function toggleShowTraffic() {
function toggleTrafficHover() {
showTrafficOnHover.classed('active', !trafficHover());
}
......@@ -1452,6 +1454,14 @@
return showTrafficOnHover.classed('active');
}
function toggleMetaSelect() {
metaToSelect.classed('active', !metaSelect());
}
function metaSelect() {
return metaToSelect.classed('active');
}
// ==============================
// View life-cycle callbacks
......@@ -1519,8 +1529,8 @@
id: d.id,
'class': d.class,
'memento': {
x: Math.floor(d.x),
y: Math.floor(d.y)
x: d.x,
y: d.y
}
});
}
......@@ -1537,7 +1547,8 @@
.linkStrength(lstrg)
.on('tick', tick);
network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd, true); // true=require meta
network.drag = d3u.createDragBehavior(network.force,
selectCb, atDragEnd, metaSelect);
// create mask layer for when we lose connection to server.
mask = view.$div.append('div').attr('id','topo-mask');
......@@ -1546,6 +1557,11 @@
para(mask, 'Try refreshing the page.');
}
function para(sel, text) {
sel.append('p').text(text);
}
function load(view, ctx, flags) {
// resize, in case the window was resized while we were not loaded
resize(view, ctx, flags);
......@@ -1647,9 +1663,6 @@
function resize(view, ctx, flags) {
setSize(svg, view);
// TODO: hook to recompute layout, perhaps? work with zoom/pan code
// adjust force layout size
}
......