Simon Hunt

GUI -- Key Bindings pop-up (new branch)

Change-Id: I544c80b1ce78c231985d7104f60b59bba3b7911e
...@@ -23,13 +23,41 @@ ...@@ -23,13 +23,41 @@
23 #keymap svg { 23 #keymap svg {
24 position: absolute; 24 position: absolute;
25 bottom: 40px; 25 bottom: 40px;
26 - opacity: 0.5; 26 + opacity: 0.7;
27 +}
28 +
29 +#keymap svg g.pane {
30 + fill: black;
27 } 31 }
28 32
29 #keymap svg text.title { 33 #keymap svg text.title {
30 font-size: 12pt; 34 font-size: 12pt;
31 font-style: italic; 35 font-style: italic;
32 - fill: #666;
33 text-anchor: middle; 36 text-anchor: middle;
37 + fill: #444;
38 +}
39 +
40 +#keymap svg g.keyItem {
41 + fill: white;
42 +}
43 +
44 +#keymap svg g.keyItem line {
45 + stroke: #444;
46 + stroke-dasharray: 4 4;
47 +}
48 +
49 +#keymap svg text {
50 + font-size: 10pt;
51 + alignment-baseline: middle;
52 +}
53 +
54 +#keymap svg text.key {
55 + font-size: 10pt;
56 + fill: #8aa;
57 +}
58 +
59 +#keymap svg text.desc {
60 + font-size: 10pt;
61 + fill: #888;
34 } 62 }
35 63
......
...@@ -33,9 +33,14 @@ ...@@ -33,9 +33,14 @@
33 var w = '100%', 33 var w = '100%',
34 h = '80%', 34 h = '80%',
35 fade = 750, 35 fade = 750,
36 - vb = '-200 -200 400 400', 36 + vb = '-220 -220 440 440',
37 - xpad = 20, 37 + paneW = 400,
38 - ypad = 10; 38 + paneH = 340,
39 + offy = 65,
40 + dy = 20,
41 + offKey = 40,
42 + offDesc = offKey + 50,
43 + lineW = paneW - (2*offKey);
39 44
40 // State variables 45 // State variables
41 var data = []; 46 var data = [];
...@@ -43,104 +48,121 @@ ...@@ -43,104 +48,121 @@
43 // DOM elements and the like 48 // DOM elements and the like
44 var kmdiv = d3.select('#keymap'); 49 var kmdiv = d3.select('#keymap');
45 50
46 - function computeBox(el) { 51 + function isA(a) {
47 - var text = el.select('text'), 52 + return $.isArray(a) ? a : null;
48 - box = text.node().getBBox(); 53 + }
49 -
50 - // center
51 - box.x = -box.width / 2;
52 - box.y = -box.height / 2;
53 54
54 - // add some padding
55 - box.x -= xpad;
56 - box.width += xpad * 2;
57 - box.y -= ypad;
58 - box.height += ypad * 2;
59 55
60 - return box; 56 + var svg = kmdiv.select('svg'),
61 - } 57 + pane;
62 58
63 - function updateKeymap() { 59 + function updateKeyItems() {
64 - var items = svg.selectAll('.bindingItem') 60 + var items = pane.selectAll('.keyItem')
65 .data(data); 61 .data(data);
66 62
67 var entering = items.enter() 63 var entering = items.enter()
68 .append('g') 64 .append('g')
69 .attr({ 65 .attr({
70 - class: 'bindingItem', 66 + id: function (d) { return d.id; },
71 - opacity: 0 67 + class: 'keyItem'
72 - }) 68 + });
73 - .transition()
74 - .duration(fade)
75 - .attr('opacity', 1);
76 69
77 - entering.each(function (d) { 70 + entering.each(function (d, i) {
78 var el = d3.select(this), 71 var el = d3.select(this),
79 - box; 72 + y = offy + dy * i;
80 - 73 +
81 - d.el = el; 74 + if (d.id === '_') {
82 - el.append('rect').attr({ rx: 10, ry: 10}); 75 + el.append('line')
83 - el.append('text').text(d.label); 76 + .attr({
84 - box = computeBox(el); 77 + class: 'sep',
85 - el.select('rect').attr(box); 78 + x1: offKey,
79 + y1: y,
80 + x2: offKey + lineW,
81 + y2: y
82 + });
83 + } else {
84 + el.append('text')
85 + .text(d.key)
86 + .attr({
87 + class: 'key',
88 + x: offKey,
89 + y: y
90 + });
91 +
92 + el.append('text')
93 + .text(d.desc)
94 + .attr({
95 + class: 'desc',
96 + x: offDesc,
97 + y: y
98 + });
99 + }
86 }); 100 });
87 -
88 - items.exit()
89 - .transition()
90 - .duration(fade)
91 - .attr({ opacity: 0})
92 - .remove();
93 } 101 }
94 102
95 - function clearFlash() { 103 + function aggregateData(bindings) {
96 - if (timer) { 104 + var gmap = d3.map(bindings.globalKeys),
97 - clearInterval(timer); 105 + vmap = d3.map(bindings.viewKeys),
98 - } 106 + gkeys = gmap.keys(),
107 + vkeys = vmap.keys();
108 +
109 + gkeys.sort();
110 + vkeys.sort();
111 +
99 data = []; 112 data = [];
100 - updateFeedback(); 113 + gkeys.forEach(function (k) {
101 - } 114 + addItem('global', k, gmap.get(k));
115 + });
116 + addItem('separator');
117 + vkeys.forEach(function (k) {
118 + addItem('view', k, vmap.get(k));
119 + });
102 120
103 - // for now, simply display some text feedback 121 + function addItem(type, k, d) {
104 - function flash(text) { 122 + var id = type + '-' + k,
105 - // cancel old scheduled event if there was one 123 + a = isA(d),
106 - if (timer) { 124 + desc = a && a[1];
107 - clearInterval(timer); 125 + if (desc) {
126 + data.push(
127 + {
128 + id: id,
129 + type: type,
130 + key: k,
131 + desc: desc
132 + }
133 + );
134 + } else if (type === 'separator') {
135 + data.push({
136 + id: '_',
137 + type: type
138 + });
139 + }
108 } 140 }
109 - timer = setInterval(function () {
110 - clearFlash();
111 - }, showFor);
112 -
113 - data = [{
114 - label: text
115 - }];
116 - updateFeedback();
117 - }
118 141
119 - // ===================================== 142 + }
120 - var svg = kmdiv.select('svg');
121 143
122 function populateBindings(bindings) { 144 function populateBindings(bindings) {
123 svg.append('g') 145 svg.append('g')
124 .attr({ 146 .attr({
125 class: 'keyBindings', 147 class: 'keyBindings',
126 - transform: 'translate(-200,-120)', 148 + transform: 'translate(-200,-200)',
127 opacity: 0 149 opacity: 0
128 }) 150 })
129 .transition() 151 .transition()
130 .duration(fade) 152 .duration(fade)
131 .attr('opacity', 1); 153 .attr('opacity', 1);
132 154
133 - var g = svg.select('g'); 155 + pane = svg.select('g');
134 156
135 - g.append('rect') 157 + pane.append('rect')
136 .attr({ 158 .attr({
137 - width: 400, 159 + width: paneW,
138 - height: 240, 160 + height: paneH,
139 rx: 8 161 rx: 8
140 }); 162 });
141 163
142 - g.append('text') 164 + pane.append('text')
143 - .text('Key Bindings') 165 + .text('Keyboard Shortcuts')
144 .attr({ 166 .attr({
145 x: 200, 167 x: 200,
146 y: 0, 168 y: 0,
...@@ -148,11 +170,12 @@ ...@@ -148,11 +170,12 @@
148 class: 'title' 170 class: 'title'
149 }); 171 });
150 172
151 - // TODO: append .keyItems to rectangle 173 + aggregateData(bindings);
174 + updateKeyItems();
152 } 175 }
153 176
154 function fadeBindings() { 177 function fadeBindings() {
155 - svg.selectAll('g') 178 + svg.selectAll('g.keyBindings')
156 .transition() 179 .transition()
157 .duration(fade) 180 .duration(fade)
158 .attr('opacity', 0); 181 .attr('opacity', 0);
...@@ -178,7 +201,8 @@ ...@@ -178,7 +201,8 @@
178 if (svg.empty()) { 201 if (svg.empty()) {
179 addSvg(); 202 addSvg();
180 populateBindings(bindings); 203 populateBindings(bindings);
181 - console.log("SHOW KEY MAP"); 204 + } else {
205 + hideKeyMap();
182 } 206 }
183 } 207 }
184 208
...@@ -187,7 +211,6 @@ ...@@ -187,7 +211,6 @@
187 if (!svg.empty()) { 211 if (!svg.empty()) {
188 fadeBindings(); 212 fadeBindings();
189 removeSvg(); 213 removeSvg();
190 - console.log("HIDE KEY MAP");
191 return true; 214 return true;
192 } 215 }
193 return false; 216 return false;
......
...@@ -413,9 +413,9 @@ ...@@ -413,9 +413,9 @@
413 413
414 function setupGlobalKeys() { 414 function setupGlobalKeys() {
415 keyHandler.globalKeys = { 415 keyHandler.globalKeys = {
416 - slash: keyMap, 416 + slash: [keyMap, 'Show / hide keyboard shortcuts'],
417 - esc: escapeKey, 417 + esc: [escapeKey, 'Dismiss dialog or cancel selections'],
418 - T: toggleTheme 418 + T: [toggleTheme, "Toggle theme"]
419 }; 419 };
420 // Masked keys are global key handlers that always return true. 420 // Masked keys are global key handlers that always return true.
421 // That is, the view will never see the event for that key. 421 // That is, the view will never see the event for that key.
...@@ -476,7 +476,8 @@ ...@@ -476,7 +476,8 @@
476 var event = d3.event, 476 var event = d3.event,
477 keyCode = event.keyCode, 477 keyCode = event.keyCode,
478 key = whatKey(keyCode), 478 key = whatKey(keyCode),
479 - gcb = isF(keyHandler.globalKeys[key]), 479 + gk = keyHandler.globalKeys[key],
480 + gcb = isF(gk) || (isA(gk) && isF(gk[0])),
480 vk = keyHandler.viewKeys[key], 481 vk = keyHandler.viewKeys[key],
481 vcb = isF(vk) || (isA(vk) && isF(vk[0])) || isF(keyHandler.viewFn); 482 vcb = isF(vk) || (isA(vk) && isF(vk[0])) || isF(keyHandler.viewFn);
482 483
......
...@@ -144,12 +144,12 @@ ...@@ -144,12 +144,12 @@
144 B: [toggleBg, 'Toggle background image'], 144 B: [toggleBg, 'Toggle background image'],
145 L: [cycleLabels, 'Cycle Device labels'], 145 L: [cycleLabels, 'Cycle Device labels'],
146 P: togglePorts, 146 P: togglePorts,
147 - U: unpin, 147 + U: [unpin, 'Unpin node'],
148 - R: resetZoomPan, 148 + R: [resetZoomPan, 'Reset zoom/pan'],
149 - H: toggleHover, 149 + H: [cycleHoverMode, 'Cycle hover mode'],
150 - V: showTrafficAction, 150 + V: [showTrafficAction, 'Show traffic'],
151 - A: showAllTrafficAction, 151 + A: [showAllTrafficAction, 'Show all traffic'],
152 - F: showDeviceLinkFlowsAction, 152 + F: [showDeviceLinkFlowsAction, 'Show device link flows'],
153 esc: handleEscape 153 esc: handleEscape
154 }; 154 };
155 155
...@@ -322,7 +322,7 @@ ...@@ -322,7 +322,7 @@
322 }); 322 });
323 } 323 }
324 324
325 - function toggleHover(view) { 325 + function cycleHoverMode(view) {
326 hoverMode++; 326 hoverMode++;
327 if (hoverMode === hoverModes.length) { 327 if (hoverMode === hoverModes.length) {
328 hoverMode = 0; 328 hoverMode = 0;
......