GUI -- Augmented pan/zoom & select/drag integration by having a toggle button for 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
diff --git a/web/gui/src/main/webapp/d3Utils.js b/web/gui/src/main/webapp/d3Utils.js
index e647a37..90b3032 100644
--- a/web/gui/src/main/webapp/d3Utils.js
+++ b/web/gui/src/main/webapp/d3Utils.js
@@ -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)) {
diff --git a/web/gui/src/main/webapp/mast2.css b/web/gui/src/main/webapp/mast2.css
index 9cf1783..fa23835 100644
--- a/web/gui/src/main/webapp/mast2.css
+++ b/web/gui/src/main/webapp/mast2.css
@@ -100,6 +100,7 @@
}
#bb .btn {
+ margin: 0 4px;
padding: 2px 6px;
font-size: 9pt;
cursor: pointer;
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index 41fcd6b..9fd92d7 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -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
}