GUI - Rebuilt Quick Help layout to allow multi-column key binding section.
Change-Id: Icada50c695ce60c8cbedb38d86434a842d935e77
Showing
3 changed files
with
190 additions
and
116 deletions
| ... | @@ -417,19 +417,23 @@ | ... | @@ -417,19 +417,23 @@ |
| 417 | } | 417 | } |
| 418 | 418 | ||
| 419 | function setupGlobalKeys() { | 419 | function setupGlobalKeys() { |
| 420 | - keyHandler.globalKeys = { | 420 | + $.extend(keyHandler, { |
| 421 | - slash: [quickHelp, 'Show / hide Quick Help'], | 421 | + globalKeys: { |
| 422 | backSlash: [quickHelp, 'Show / hide Quick Help'], | 422 | backSlash: [quickHelp, 'Show / hide Quick Help'], |
| 423 | + slash: [quickHelp, 'Show / hide Quick Help'], | ||
| 423 | esc: [escapeKey, 'Dismiss dialog or cancel selections'], | 424 | esc: [escapeKey, 'Dismiss dialog or cancel selections'], |
| 424 | T: [toggleTheme, "Toggle theme"] | 425 | T: [toggleTheme, "Toggle theme"] |
| 425 | - }; | 426 | + }, |
| 427 | + globalFormat: ['backSlash', 'slash', 'esc', 'T'], | ||
| 428 | + | ||
| 426 | // Masked keys are global key handlers that always return true. | 429 | // Masked keys are global key handlers that always return true. |
| 427 | // That is, the view will never see the event for that key. | 430 | // That is, the view will never see the event for that key. |
| 428 | - keyHandler.maskedKeys = { | 431 | + maskedKeys: { |
| 429 | slash: true, | 432 | slash: true, |
| 430 | backSlash: true, | 433 | backSlash: true, |
| 431 | T: true | 434 | T: true |
| 432 | - }; | 435 | + } |
| 436 | + }); | ||
| 433 | } | 437 | } |
| 434 | 438 | ||
| 435 | function quickHelp(view, key, code, ev) { | 439 | function quickHelp(view, key, code, ev) { | ... | ... |
| ... | @@ -26,7 +26,7 @@ | ... | @@ -26,7 +26,7 @@ |
| 26 | 26 | ||
| 27 | #quickhelp svg { | 27 | #quickhelp svg { |
| 28 | position: absolute; | 28 | position: absolute; |
| 29 | - bottom: 40px; | 29 | + top: 180px; |
| 30 | opacity: 1; | 30 | opacity: 1; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| ... | @@ -36,7 +36,7 @@ | ... | @@ -36,7 +36,7 @@ |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | #quickhelp svg text.title { | 38 | #quickhelp svg text.title { |
| 39 | - font-size: 8pt; | 39 | + font-size: 10pt; |
| 40 | font-style: italic; | 40 | font-style: italic; |
| 41 | text-anchor: middle; | 41 | text-anchor: middle; |
| 42 | fill: #999; | 42 | fill: #999; |
| ... | @@ -46,23 +46,21 @@ | ... | @@ -46,23 +46,21 @@ |
| 46 | fill: white; | 46 | fill: white; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | -#quickhelp svg g.keyItem line { | 49 | +#quickhelp svg g line.qhrowsep { |
| 50 | stroke: #888; | 50 | stroke: #888; |
| 51 | stroke-dasharray: 2 2; | 51 | stroke-dasharray: 2 2; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | #quickhelp svg text { | 54 | #quickhelp svg text { |
| 55 | - font-size: 5pt; | 55 | + font-size: 7pt; |
| 56 | alignment-baseline: middle; | 56 | alignment-baseline: middle; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | #quickhelp svg text.key { | 59 | #quickhelp svg text.key { |
| 60 | - font-size: 5pt; | ||
| 61 | fill: #add; | 60 | fill: #add; |
| 62 | } | 61 | } |
| 63 | 62 | ||
| 64 | #quickhelp svg text.desc { | 63 | #quickhelp svg text.desc { |
| 65 | - font-size: 5pt; | ||
| 66 | fill: #ddd; | 64 | fill: #ddd; |
| 67 | } | 65 | } |
| 68 | 66 | ... | ... |
| ... | @@ -26,29 +26,35 @@ | ... | @@ -26,29 +26,35 @@ |
| 26 | (function (onos){ | 26 | (function (onos){ |
| 27 | 'use strict'; | 27 | 'use strict'; |
| 28 | 28 | ||
| 29 | - // API's | ||
| 30 | - var api = onos.api; | ||
| 31 | - | ||
| 32 | // Config variables | 29 | // Config variables |
| 33 | var w = '100%', | 30 | var w = '100%', |
| 34 | h = '80%', | 31 | h = '80%', |
| 35 | fade = 500, | 32 | fade = 500, |
| 36 | vb = '-200 0 400 400'; | 33 | vb = '-200 0 400 400'; |
| 37 | 34 | ||
| 35 | + // layout configuration | ||
| 36 | + var pad = 10, | ||
| 37 | + offy = 45, | ||
| 38 | + sepYDelta = 20, | ||
| 39 | + colXDelta = 16, | ||
| 40 | + yTextSpc = 12, | ||
| 41 | + offDesc = 8; | ||
| 42 | + | ||
| 38 | // State variables | 43 | // State variables |
| 39 | - var data = []; | 44 | + var data = [], |
| 45 | + yCount; | ||
| 40 | 46 | ||
| 41 | // DOM elements and the like | 47 | // DOM elements and the like |
| 42 | var qhdiv = d3.select('#quickhelp'), | 48 | var qhdiv = d3.select('#quickhelp'), |
| 43 | svg = qhdiv.select('svg'), | 49 | svg = qhdiv.select('svg'), |
| 44 | - pane, | 50 | + pane, rect, items; |
| 45 | - rect, | ||
| 46 | - items, | ||
| 47 | - keyAgg; | ||
| 48 | 51 | ||
| 49 | // General functions | 52 | // General functions |
| 50 | - function isA(a) { | 53 | + function isA(a) { return $.isArray(a) ? a : null; } |
| 51 | - return $.isArray(a) ? a : null; | 54 | + function isS(s) { return typeof s === 'string'; } |
| 55 | + | ||
| 56 | + function cap(s) { | ||
| 57 | + return s.replace(/^[a-z]/, function (m) { return m.toUpperCase(); }); | ||
| 52 | } | 58 | } |
| 53 | 59 | ||
| 54 | var keyDisp = { | 60 | var keyDisp = { |
| ... | @@ -63,144 +69,212 @@ | ... | @@ -63,144 +69,212 @@ |
| 63 | downArrow: 'D-arrow' | 69 | downArrow: 'D-arrow' |
| 64 | }; | 70 | }; |
| 65 | 71 | ||
| 66 | - function cap(s) { | ||
| 67 | - return s.replace(/^[a-z]/, function (m) { return m.toUpperCase(); }); | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | function mkKeyDisp(id) { | 72 | function mkKeyDisp(id) { |
| 71 | var v = keyDisp[id] || id; | 73 | var v = keyDisp[id] || id; |
| 72 | return cap(v); | 74 | return cap(v); |
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | - // layout configuration | 77 | + function addSeparator(el, i) { |
| 76 | - var pad = 8, | 78 | + var y = sepYDelta/2 - 5; |
| 77 | - offy = 45, | 79 | + el.append('line') |
| 78 | - dy = 10, | 80 | + .attr({ 'class': 'qhrowsep', x1: 0, y1: y, x2: 0, y2: y }); |
| 79 | - offDesc = 8; | 81 | + } |
| 80 | 82 | ||
| 81 | - // D3 magic | 83 | + function addContent(el, data, ri) { |
| 82 | - function updateKeyItems() { | 84 | + var xCount = 0, |
| 83 | - var keyItems = items.selectAll('.keyItem') | 85 | + clsPfx = 'qh-r' + ri + '-c'; |
| 84 | - .data(data); | 86 | + |
| 87 | + function addColumn(el, c, i) { | ||
| 88 | + var cls = clsPfx + i, | ||
| 89 | + oy = 0, | ||
| 90 | + aggKey = el.append('g').attr('visibility', 'hidden'), | ||
| 91 | + gcol = el.append('g').attr({ | ||
| 92 | + 'class': cls, | ||
| 93 | + transform: translate(xCount, 0) | ||
| 94 | + }); | ||
| 85 | 95 | ||
| 86 | - var entering = keyItems.enter() | 96 | + c.forEach(function (j) { |
| 87 | - .append('g') | 97 | + var k = j[0], |
| 98 | + v = j[1]; | ||
| 99 | + | ||
| 100 | + if (k !== '-') { | ||
| 101 | + aggKey.append('text').text(k); | ||
| 102 | + | ||
| 103 | + gcol.append('text').text(k) | ||
| 104 | + .attr({ | ||
| 105 | + 'class': 'key', | ||
| 106 | + y: oy | ||
| 107 | + }); | ||
| 108 | + gcol.append('text').text(v) | ||
| 88 | .attr({ | 109 | .attr({ |
| 89 | - id: function (d) { return d.id; }, | 110 | + 'class': 'desc', |
| 90 | - class: 'keyItem' | 111 | + y: oy |
| 91 | }); | 112 | }); |
| 113 | + } | ||
| 92 | 114 | ||
| 93 | - entering.each(function (d, i) { | 115 | + oy += yTextSpc; |
| 94 | - var el = d3.select(this), | 116 | + }); |
| 95 | - y = offy + dy * i; | ||
| 96 | 117 | ||
| 97 | - if (d.id[0] === '_') { | 118 | + // adjust position of descriptions, based on widest key |
| 98 | - el.append('line') | 119 | + var kbox = aggKey.node().getBBox(), |
| 99 | - .attr({ x1: 0, y1: y, x2: 1, y2: y}); | 120 | + ox = kbox.width + offDesc; |
| 100 | - } else { | 121 | + gcol.selectAll('.desc').attr('x', ox); |
| 101 | - el.append('text') | 122 | + aggKey.remove(); |
| 102 | - .text(d.key) | 123 | + |
| 103 | - .attr({ | 124 | + // now update x-offset for next column |
| 104 | - class: 'key', | 125 | + var bbox = gcol.node().getBBox(); |
| 105 | - x: 0, | 126 | + xCount += bbox.width + colXDelta; |
| 106 | - y: y | 127 | + } |
| 128 | + | ||
| 129 | + data.forEach(function (d, i) { | ||
| 130 | + addColumn(el, d, i); | ||
| 107 | }); | 131 | }); |
| 108 | - // NOTE: used for sizing column width... | ||
| 109 | - keyAgg.append('text').text(d.key).attr('class', 'key'); | ||
| 110 | 132 | ||
| 111 | - el.append('text') | 133 | + // finally, return the height of the row.. |
| 112 | - .text(d.desc) | 134 | + return el.node().getBBox().height; |
| 135 | + } | ||
| 136 | + | ||
| 137 | + function updateKeyItems() { | ||
| 138 | + var rows = items.selectAll('.qhRow').data(data); | ||
| 139 | + | ||
| 140 | + yCount = offy; | ||
| 141 | + | ||
| 142 | + var entering = rows.enter() | ||
| 143 | + .append('g') | ||
| 113 | .attr({ | 144 | .attr({ |
| 114 | - class: 'desc', | 145 | + 'class': 'qhrow' |
| 115 | - x: offDesc, | ||
| 116 | - y: y | ||
| 117 | }); | 146 | }); |
| 147 | + | ||
| 148 | + entering.each(function (r, i) { | ||
| 149 | + var el = d3.select(this), | ||
| 150 | + sep = r.type === 'sep', | ||
| 151 | + dy; | ||
| 152 | + | ||
| 153 | + el.attr('transform', translate(0, yCount)); | ||
| 154 | + | ||
| 155 | + if (sep) { | ||
| 156 | + addSeparator(el, i); | ||
| 157 | + yCount += sepYDelta; | ||
| 158 | + } else { | ||
| 159 | + dy = addContent(el, r.data, i); | ||
| 160 | + yCount += dy; | ||
| 118 | } | 161 | } |
| 119 | }); | 162 | }); |
| 120 | 163 | ||
| 121 | - var kbox = keyAgg.node().getBBox(); | 164 | + // size the backing rectangle |
| 122 | - items.selectAll('.desc').attr('x', kbox.width + offDesc); | 165 | + var ibox = items.node().getBBox(), |
| 166 | + paneW = ibox.width + pad * 2, | ||
| 167 | + paneH = ibox.height + offy; | ||
| 123 | 168 | ||
| 124 | - var box = items.node().getBBox(), | 169 | + items.selectAll('.qhrowsep').attr('x2', ibox.width); |
| 125 | - paneW = box.width + pad * 2, | ||
| 126 | - paneH = box.height + offy; | ||
| 127 | - | ||
| 128 | - items.selectAll('line').attr('x2', box.width); | ||
| 129 | items.attr('transform', translate(-paneW/2, -pad)); | 170 | items.attr('transform', translate(-paneW/2, -pad)); |
| 130 | rect.attr({ | 171 | rect.attr({ |
| 131 | width: paneW, | 172 | width: paneW, |
| 132 | height: paneH, | 173 | height: paneH, |
| 133 | transform: translate(-paneW/2-pad, 0) | 174 | transform: translate(-paneW/2-pad, 0) |
| 134 | }); | 175 | }); |
| 176 | + | ||
| 135 | } | 177 | } |
| 136 | 178 | ||
| 137 | function translate(x, y) { | 179 | function translate(x, y) { |
| 138 | return 'translate(' + x + ',' + y + ')'; | 180 | return 'translate(' + x + ',' + y + ')'; |
| 139 | } | 181 | } |
| 140 | 182 | ||
| 183 | + function checkFmt(fmt) { | ||
| 184 | + // should be a single array of keys, | ||
| 185 | + // or array of arrays of keys (one per column). | ||
| 186 | + // return null if there is a problem. | ||
| 187 | + var a = isA(fmt), | ||
| 188 | + n = a && a.length, | ||
| 189 | + ns = 0, | ||
| 190 | + na = 0; | ||
| 191 | + | ||
| 192 | + if (n) { | ||
| 193 | + // it is an array which has some content | ||
| 194 | + a.forEach(function (d) { | ||
| 195 | + isA(d) && na++; | ||
| 196 | + isS(d) && ns++; | ||
| 197 | + }); | ||
| 198 | + if (na === n || ns === n) { | ||
| 199 | + // all arrays or all strings... | ||
| 200 | + return a; | ||
| 201 | + } | ||
| 202 | + } | ||
| 203 | + return null; | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + function buildBlock(map, fmt) { | ||
| 207 | + var b = []; | ||
| 208 | + fmt.forEach(function (k) { | ||
| 209 | + var v = map.get(k), | ||
| 210 | + a = isA(v), | ||
| 211 | + d = (a && a[1]); | ||
| 212 | + | ||
| 213 | + // '-' marks a separator; d is the description | ||
| 214 | + if (k === '-' || d) { | ||
| 215 | + b.push([mkKeyDisp(k), d]); | ||
| 216 | + } | ||
| 217 | + }); | ||
| 218 | + return b; | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + function emptyRow() { | ||
| 222 | + return { type: 'row', data: []}; | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + function mkArrRow(fmt) { | ||
| 226 | + var d = emptyRow(); | ||
| 227 | + d.data.push(fmt); | ||
| 228 | + return d; | ||
| 229 | + } | ||
| 230 | + | ||
| 231 | + function mkColumnarRow(map, fmt) { | ||
| 232 | + var d = emptyRow(); | ||
| 233 | + fmt.forEach(function (a) { | ||
| 234 | + d.data.push(buildBlock(map, a)); | ||
| 235 | + }); | ||
| 236 | + return d; | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + function mkMapRow(map, fmt) { | ||
| 240 | + var d = emptyRow(); | ||
| 241 | + d.data.push(buildBlock(map, fmt)); | ||
| 242 | + return d; | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + function addRow(row) { | ||
| 246 | + var d = row || { type: 'sep' }; | ||
| 247 | + data.push(d); | ||
| 248 | + } | ||
| 249 | + | ||
| 141 | function aggregateData(bindings) { | 250 | function aggregateData(bindings) { |
| 142 | var hf = '_helpFormat', | 251 | var hf = '_helpFormat', |
| 143 | gmap = d3.map(bindings.globalKeys), | 252 | gmap = d3.map(bindings.globalKeys), |
| 253 | + gfmt = bindings.globalFormat, | ||
| 144 | vmap = d3.map(bindings.viewKeys), | 254 | vmap = d3.map(bindings.viewKeys), |
| 145 | - fmt = vmap.get(hf), | ||
| 146 | vgest = bindings.viewGestures, | 255 | vgest = bindings.viewGestures, |
| 147 | - gkeys = gmap.keys(), | 256 | + vfmt, vkeys; |
| 148 | - vkeys, | ||
| 149 | - sep = 0; | ||
| 150 | 257 | ||
| 151 | // filter out help format entry | 258 | // filter out help format entry |
| 259 | + vfmt = checkFmt(vmap.get(hf)); | ||
| 152 | vmap.remove(hf); | 260 | vmap.remove(hf); |
| 153 | - vkeys = vmap.keys(), | ||
| 154 | - | ||
| 155 | - gkeys.sort(); | ||
| 156 | - vkeys.sort(); | ||
| 157 | 261 | ||
| 158 | - data = []; | 262 | + // if bad (or no) format, fallback to sorted keys |
| 159 | - gkeys.forEach(function (k) { | 263 | + if (!vfmt) { |
| 160 | - addItem('glob', k, gmap.get(k)); | 264 | + vkeys = vmap.keys(); |
| 161 | - }); | 265 | + vfmt = vkeys.sort(); |
| 162 | - addItem('sep'); | ||
| 163 | - vkeys.forEach(function (k) { | ||
| 164 | - addItem('view', k, vmap.get(k)); | ||
| 165 | - }); | ||
| 166 | - addItem('sep'); | ||
| 167 | - vgest.forEach(function (g) { | ||
| 168 | - if (g.length === 2) { | ||
| 169 | - addItem('gest', g[0], g[1]); | ||
| 170 | } | 266 | } |
| 171 | - }); | ||
| 172 | - | ||
| 173 | 267 | ||
| 174 | - function addItem(type, k, d) { | 268 | + data = []; |
| 175 | - var id = type + '-' + k, | ||
| 176 | - a = isA(d), | ||
| 177 | - desc = a && a[1]; | ||
| 178 | 269 | ||
| 179 | - if (type === 'sep') { | 270 | + addRow(mkMapRow(gmap, gfmt)); |
| 180 | - data.push({ | 271 | + addRow(); |
| 181 | - id: '_' + sep++, | 272 | + addRow(isA(vfmt[0]) ? mkColumnarRow(vmap, vfmt) : mkMapRow(vmap, vfmt)); |
| 182 | - type: type | 273 | + addRow(); |
| 183 | - }); | 274 | + addRow(mkArrRow(vgest)); |
| 184 | - } else if (type === 'gest') { | ||
| 185 | - data.push({ | ||
| 186 | - id: id, | ||
| 187 | - type: type, | ||
| 188 | - key: k, | ||
| 189 | - desc: d | ||
| 190 | - }); | ||
| 191 | - } else if (desc) { | ||
| 192 | - data.push( | ||
| 193 | - { | ||
| 194 | - id: id, | ||
| 195 | - type: type, | ||
| 196 | - key: mkKeyDisp(k), | ||
| 197 | - desc: desc | ||
| 198 | - } | ||
| 199 | - ); | ||
| 200 | - } | ||
| 201 | - } | ||
| 202 | } | 275 | } |
| 203 | 276 | ||
| 277 | + | ||
| 204 | function popBind(bindings) { | 278 | function popBind(bindings) { |
| 205 | pane = svg.append('g') | 279 | pane = svg.append('g') |
| 206 | .attr({ | 280 | .attr({ |
| ... | @@ -220,8 +294,6 @@ | ... | @@ -220,8 +294,6 @@ |
| 220 | }); | 294 | }); |
| 221 | 295 | ||
| 222 | items = pane.append('g'); | 296 | items = pane.append('g'); |
| 223 | - keyAgg = pane.append('g').style('visibility', 'hidden'); | ||
| 224 | - | ||
| 225 | aggregateData(bindings); | 297 | aggregateData(bindings); |
| 226 | updateKeyItems(); | 298 | updateKeyItems(); |
| 227 | 299 | ... | ... |
-
Please register or login to post a comment