Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview Interactive visualizaiton of TimelineModel objects | 6 * @fileoverview Interactive visualizaiton of TimelineModel objects |
| 7 * based loosely on gantt charts. Each thread in the TimelineModel is given a | 7 * based loosely on gantt charts. Each thread in the TimelineModel is given a |
| 8 * set of TimelineTracks, one per subrow in the thread. The Timeline class | 8 * set of TimelineTracks, one per subrow in the thread. The Timeline class |
| 9 * acts as a controller, creating the individual tracks, while TimelineTracks | 9 * acts as a controller, creating the individual tracks, while TimelineTracks |
| 10 * do actual drawing. | 10 * do actual drawing. |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 128 get gridStep() { | 128 get gridStep() { |
| 129 return this.gridStep_; | 129 return this.gridStep_; |
| 130 }, | 130 }, |
| 131 | 131 |
| 132 applyTransformToCanavs: function(ctx) { | 132 applyTransformToCanavs: function(ctx) { |
| 133 ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0); | 133 ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0); |
| 134 } | 134 } |
| 135 }; | 135 }; |
| 136 | 136 |
| 137 /** | 137 /** |
| 138 * Uses an embedded iframe to measure provided elements without forcing layout | |
| 139 * on the main document. | |
| 140 * @constructor | |
| 141 * @extends {Object} | |
| 142 */ | |
| 143 function MeasuringStick() | |
| 144 { | |
| 145 var iframe = document.createElement('iframe'); | |
| 146 iframe.style.cssText = 'width:100%;height:0;border:0;visibility:hidden'; | |
| 147 document.body.appendChild(iframe); | |
| 148 this._doc = iframe.contentDocument; | |
| 149 this._window = iframe.contentWindow; | |
| 150 this._doc.body.style.cssText = 'padding:0;margin:0;overflow:hidden'; | |
| 151 [].forEach.call(document.querySelectorAll('link[rel=stylesheet]'), | |
|
jbates
2011/10/21 20:21:05
What's this do?
| |
| 152 function(stylesheet) { | |
| 153 var link = this._doc.createElement('link'); | |
| 154 link.rel = 'stylesheet'; | |
| 155 link.href = stylesheet.href; | |
| 156 this._doc.head.appendChild(link); | |
| 157 }, this); | |
| 158 } | |
| 159 | |
| 160 MeasuringStick.prototype = { | |
| 161 __proto__: Object.prototype, | |
| 162 | |
| 163 /** | |
| 164 * Converts measures like 50px to their size in pixels, if possible. | |
| 165 */ | |
| 166 convertMeasureToPixels_: function(str) { | |
| 167 var g = /(\d+)px/.exec(str); | |
| 168 if (g) | |
| 169 return parseInt(g[0]); | |
| 170 throw 'Unrecognized unit on ' + str; | |
| 171 }, | |
| 172 | |
| 173 /** | |
| 174 * Measures the provided element without forcing layout on the main | |
| 175 * document. | |
| 176 */ | |
| 177 measure: function(element) | |
| 178 { | |
| 179 this._doc.body.appendChild(element); | |
| 180 var style = this._window.getComputedStyle(element); | |
| 181 var width = this.convertMeasureToPixels_(style.width); | |
| 182 var height = this.convertMeasureToPixels_(style.height); | |
| 183 this._doc.body.removeChild(element); | |
| 184 return { width: width, height: height }; | |
| 185 } | |
| 186 }; | |
| 187 | |
| 188 /** | |
| 138 * Renders a TimelineModel into a div element, making one | 189 * Renders a TimelineModel into a div element, making one |
| 139 * TimelineTrack for each subrow in each thread of the model, managing | 190 * TimelineTrack for each subrow in each thread of the model, managing |
| 140 * overall track layout, and handling user interaction with the | 191 * overall track layout, and handling user interaction with the |
| 141 * viewport. | 192 * viewport. |
| 142 * | 193 * |
| 143 * @constructor | 194 * @constructor |
| 144 * @extends {HTMLDivElement} | 195 * @extends {HTMLDivElement} |
| 145 */ | 196 */ |
| 146 Timeline = cr.ui.define('div'); | 197 Timeline = cr.ui.define('div'); |
| 147 | 198 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 160 | 211 |
| 161 this.invalidatePending_ = false; | 212 this.invalidatePending_ = false; |
| 162 | 213 |
| 163 this.tracks_ = this.ownerDocument.createElement('div'); | 214 this.tracks_ = this.ownerDocument.createElement('div'); |
| 164 this.tracks_.invalidate = this.invalidate.bind(this); | 215 this.tracks_.invalidate = this.invalidate.bind(this); |
| 165 this.appendChild(this.tracks_); | 216 this.appendChild(this.tracks_); |
| 166 | 217 |
| 167 this.dragBox_ = this.ownerDocument.createElement('div'); | 218 this.dragBox_ = this.ownerDocument.createElement('div'); |
| 168 this.dragBox_.className = 'timeline-drag-box'; | 219 this.dragBox_.className = 'timeline-drag-box'; |
| 169 this.appendChild(this.dragBox_); | 220 this.appendChild(this.dragBox_); |
| 221 this.hideDragBox_(); | |
| 170 | 222 |
| 171 // The following code uses a setInterval to monitor the timeline control | 223 // The following code uses a setInterval to monitor the timeline control |
| 172 // for size changes. This is so that we can keep the canvas' bitmap size | 224 // for size changes. This is so that we can keep the canvas' bitmap size |
| 173 // correctly synchronized with its presentation size. | 225 // correctly synchronized with its presentation size. |
| 174 // TODO(nduca): detect this in a more efficient way, e.g. iframe hack. | 226 // TODO(nduca): detect this in a more efficient way, e.g. iframe hack. |
| 175 this.lastSize_ = this.clientWidth + 'x' + this.clientHeight; | 227 this.lastSize_ = this.clientWidth + 'x' + this.clientHeight; |
| 176 this.ownerDocument.defaultView.setInterval(function() { | 228 this.checkForResizeInterval_ = |
| 229 this.ownerDocument.defaultView.setInterval(function() { | |
| 230 if (!this.isAttachedToDocument_) | |
| 231 return; | |
| 177 var curSize = this.clientWidth + 'x' + this.clientHeight; | 232 var curSize = this.clientWidth + 'x' + this.clientHeight; |
| 178 if (this.clientWidth && curSize != this.lastSize_) { | 233 if (this.clientWidth && curSize != this.lastSize_) { |
| 179 this.lastSize_ = curSize; | 234 this.lastSize_ = curSize; |
| 180 this.onResize(); | 235 this.onResize(); |
| 181 } | 236 } |
| 182 }.bind(this), 250); | 237 }.bind(this), 250); |
| 183 | 238 |
| 184 document.addEventListener('keypress', this.onKeypress_.bind(this)); | 239 this.safeAddEventListener(document, 'keypress', this.onKeypress_, this); |
| 185 document.addEventListener('keydown', this.onKeydown_.bind(this)); | 240 this.safeAddEventListener(document, 'keydown', this.onKeydown_, this); |
| 186 document.addEventListener('mousedown', this.onMouseDown_.bind(this)); | 241 this.safeAddEventListener(document, 'mousedown', this.onMouseDown_, this); |
| 187 document.addEventListener('mousemove', this.onMouseMove_.bind(this)); | 242 this.safeAddEventListener(document, 'mousemove', this.onMouseMove_, this); |
| 188 document.addEventListener('mouseup', this.onMouseUp_.bind(this)); | 243 this.safeAddEventListener(document, 'mouseup', this.onMouseUp_, this); |
| 189 document.addEventListener('dblclick', this.onDblClick_.bind(this)); | 244 this.safeAddEventListener(document, 'dblclick', this.onDblClick_, this); |
| 190 | 245 |
| 191 this.lastMouseViewPos_ = {x: 0, y: 0}; | 246 this.lastMouseViewPos_ = {x: 0, y: 0}; |
| 192 | 247 |
| 193 this.selection_ = []; | 248 this.selection_ = []; |
| 194 }, | 249 }, |
| 195 | 250 |
| 251 safeAddEventListener: function(object, event, func, target) { | |
| 252 if (!this.boundListeners_) | |
| 253 this.boundListeners_ = []; | |
| 254 var boundFunc = func.bind(target); | |
| 255 this.boundListeners_.push({object: object, | |
| 256 event: event, | |
| 257 boundFunc: boundFunc}); | |
| 258 object.addEventListener(event, boundFunc); | |
| 259 }, | |
| 260 | |
| 261 detach: function() { | |
| 262 for (var i = 0; i < this.boundListeners_.length; ++i) { | |
| 263 var binding = this.boundListeners_[i]; | |
| 264 binding.object.removeEventListener(binding.event, binding.boundFunc); | |
| 265 } | |
| 266 this.boundListeners_ = undefined; | |
| 267 window.clearInterval(this.checkForResizeInterval_); | |
| 268 this.checkForResizeInterval_ = undefined; | |
| 269 }, | |
| 270 | |
| 196 get model() { | 271 get model() { |
| 197 return this.model_; | 272 return this.model_; |
| 198 }, | 273 }, |
| 199 | 274 |
| 200 set model(model) { | 275 set model(model) { |
| 201 if (!model) | 276 if (!model) |
| 202 throw Error('Model cannot be null'); | 277 throw Error('Model cannot be null'); |
| 203 if (this.model) { | 278 if (this.model) { |
| 204 throw Error('Cannot set model twice.'); | 279 throw Error('Cannot set model twice.'); |
| 205 } | 280 } |
| 206 this.model_ = model; | 281 this.model_ = model; |
| 207 | 282 |
| 208 // Create tracks. | 283 // Create tracks and measure their heading size |
| 209 this.tracks_.textContent = ''; | |
| 210 var threads = model.getAllThreads(); | 284 var threads = model.getAllThreads(); |
| 211 threads.sort(tracing.TimelineThread.compare); | 285 var maxHeadingWidth = 0; |
| 286 var tracks = []; | |
| 287 var measuringStick = new MeasuringStick(); | |
| 288 var headingEl = document.createElement('div'); | |
| 289 headingEl.style.position = 'fixed'; | |
| 290 headingEl.className = 'timeline-slice-track-title'; | |
| 212 for (var tI = 0; tI < threads.length; tI++) { | 291 for (var tI = 0; tI < threads.length; tI++) { |
| 213 var thread = threads[tI]; | 292 var thread = threads[tI]; |
| 214 var track = new TimelineThreadTrack(); | 293 var track = new TimelineThreadTrack(); |
| 215 track.thread = thread; | 294 track.thread = thread; |
| 216 track.viewport = this.viewport_; | 295 track.viewport = this.viewport_; |
| 296 tracks.push(track); | |
| 297 headingEl.textContent = track.heading; | |
| 298 var w = measuringStick.measure(headingEl).width; | |
| 299 // limit heading width to 300px | |
| 300 if (w > 300) | |
| 301 w = 300; | |
| 302 if (w > maxHeadingWidth) | |
| 303 maxHeadingWidth = w; | |
| 304 } | |
| 305 maxHeadingWidth = (maxHeadingWidth + 4) + 'px'; | |
|
jbates
2011/10/21 20:21:05
nit: Is this normal practice to turn a num variabl
| |
| 306 | |
| 307 // Attach tracks and set width | |
| 308 this.tracks_.textContent = ''; | |
| 309 threads.sort(tracing.TimelineThread.compare); | |
| 310 for (var tI = 0; tI < tracks.length; tI++) { | |
| 311 var track = tracks[tI]; | |
| 312 track.headingWidth = maxHeadingWidth; | |
| 217 this.tracks_.appendChild(track); | 313 this.tracks_.appendChild(track); |
| 218 | |
| 219 } | 314 } |
| 220 | 315 |
| 221 this.needsViewportReset_ = true; | 316 if (this.isAttachedToDocument_) |
| 317 this.onResize(); | |
| 318 else | |
| 319 this.needsViewportReset_ = true; | |
| 222 }, | 320 }, |
| 223 | 321 |
| 224 viewportChange_: function() { | 322 viewportChange_: function() { |
| 225 this.invalidate(); | 323 this.invalidate(); |
| 226 }, | 324 }, |
| 227 | 325 |
| 228 invalidate: function() { | 326 invalidate: function() { |
| 229 if (this.invalidatePending_) | 327 if (this.invalidatePending_) |
| 230 return; | 328 return; |
| 231 this.invalidatePending_ = true; | 329 this.invalidatePending_ = true; |
| 232 window.setTimeout(function() { | 330 if (this.isAttachedToDocument_) |
| 233 this.invalidatePending_ = false; | 331 window.setTimeout(function() { |
| 234 this.redrawAllTracks_(); | 332 this.invalidatePending_ = false; |
| 235 }.bind(this), 0); | 333 this.redrawAllTracks_(); |
| 334 }.bind(this), 0); | |
| 335 }, | |
| 336 | |
| 337 get isAttachedToDocument_() { | |
| 338 var cur = this; | |
| 339 while (cur.parentNode) | |
| 340 cur = cur.parentNode; | |
| 341 return cur == this.ownerDocument; | |
| 236 }, | 342 }, |
| 237 | 343 |
| 238 onResize: function() { | 344 onResize: function() { |
| 345 if (!this.isAttachedToDocument_) | |
| 346 throw 'Not attached to document!'; | |
| 239 for (var i = 0; i < this.tracks_.children.length; ++i) { | 347 for (var i = 0; i < this.tracks_.children.length; ++i) { |
| 240 var track = this.tracks_.children[i]; | 348 var track = this.tracks_.children[i]; |
| 241 track.onResize(); | 349 track.onResize(); |
| 242 } | 350 } |
| 351 if (this.invalidatePending_) { | |
| 352 this.invalidatePending_ = false; | |
| 353 this.redrawAllTracks_(); | |
| 354 } | |
| 243 }, | 355 }, |
| 244 | 356 |
| 245 redrawAllTracks_: function() { | 357 redrawAllTracks_: function() { |
| 246 if (this.needsViewportReset_ && this.clientWidth != 0) { | 358 if (this.needsViewportReset_ && this.clientWidth != 0) { |
| 359 if (!this.isAttachedToDocument_) | |
| 360 throw 'Not attached to document!'; | |
| 247 this.needsViewportReset_ = false; | 361 this.needsViewportReset_ = false; |
| 248 /* update viewport */ | 362 /* update viewport */ |
| 249 var rangeTimestamp = this.model_.maxTimestamp - | 363 var rangeTimestamp = this.model_.maxTimestamp - |
| 250 this.model_.minTimestamp; | 364 this.model_.minTimestamp; |
| 251 var w = this.firstCanvas.width; | 365 var w = this.firstCanvas.width; |
| 252 console.log('viewport was reset with w=', w); | |
| 253 var scaleX = w / rangeTimestamp; | 366 var scaleX = w / rangeTimestamp; |
| 254 var panX = -this.model_.minTimestamp; | 367 var panX = -this.model_.minTimestamp; |
| 255 this.viewport_.setPanAndScale(panX, scaleX); | 368 this.viewport_.setPanAndScale(panX, scaleX); |
| 256 } | 369 } |
| 257 for (var i = 0; i < this.tracks_.children.length; ++i) { | 370 for (var i = 0; i < this.tracks_.children.length; ++i) { |
| 258 this.tracks_.children[i].redraw(); | 371 this.tracks_.children[i].redraw(); |
| 259 } | 372 } |
| 260 }, | 373 }, |
| 261 | 374 |
| 262 updateChildViewports_: function() { | 375 updateChildViewports_: function() { |
| 263 for (var cI = 0; cI < this.tracks_.children.length; ++cI) { | 376 for (var cI = 0; cI < this.tracks_.children.length; ++cI) { |
| 264 var child = this.tracks_.children[cI]; | 377 var child = this.tracks_.children[cI]; |
| 265 child.setViewport(this.panX, this.scaleX); | 378 child.setViewport(this.panX, this.scaleX); |
| 266 } | 379 } |
| 267 }, | 380 }, |
| 268 | 381 |
| 382 get listenToKeys_() { | |
| 383 if (this.parentElement.parentElement.tabIndex >= 0) | |
| 384 return document.activeElement == this.parentElement.parentElement; | |
| 385 return true; | |
| 386 }, | |
| 387 | |
| 269 onKeypress_: function(e) { | 388 onKeypress_: function(e) { |
| 270 var vp = this.viewport_; | 389 var vp = this.viewport_; |
| 271 if (!this.firstCanvas) | 390 if (!this.firstCanvas) |
| 272 return; | 391 return; |
| 392 if (!this.listenToKeys_) | |
| 393 return; | |
| 273 var viewWidth = this.firstCanvas.clientWidth; | 394 var viewWidth = this.firstCanvas.clientWidth; |
| 274 var curMouseV, curCenterW; | 395 var curMouseV, curCenterW; |
| 275 switch (e.keyCode) { | 396 switch (e.keyCode) { |
| 276 case 101: // e | 397 case 101: // e |
| 277 var vX = this.lastMouseViewPos_.x; | 398 var vX = this.lastMouseViewPos_.x; |
| 278 var wX = vp.xViewToWorld(this.lastMouseViewPos_.x); | 399 var wX = vp.xViewToWorld(this.lastMouseViewPos_.x); |
| 279 var distFromCenter = vX - (viewWidth / 2); | 400 var distFromCenter = vX - (viewWidth / 2); |
| 280 var percFromCenter = distFromCenter / viewWidth; | 401 var percFromCenter = distFromCenter / viewWidth; |
| 281 var percFromCenterSq = percFromCenter * percFromCenter; | 402 var percFromCenterSq = percFromCenter * percFromCenter; |
| 282 vp.xPanWorldPosToViewPos(wX, 'center', viewWidth); | 403 vp.xPanWorldPosToViewPos(wX, 'center', viewWidth); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 309 vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5); | 430 vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5); |
| 310 break; | 431 break; |
| 311 case 68: // D | 432 case 68: // D |
| 312 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); | 433 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); |
| 313 break; | 434 break; |
| 314 } | 435 } |
| 315 }, | 436 }, |
| 316 | 437 |
| 317 // Not all keys send a keypress. | 438 // Not all keys send a keypress. |
| 318 onKeydown_: function(e) { | 439 onKeydown_: function(e) { |
| 440 if (!this.listenToKeys_) | |
| 441 return; | |
| 319 switch (e.keyCode) { | 442 switch (e.keyCode) { |
| 320 case 37: // left arrow | 443 case 37: // left arrow |
| 321 this.selectPrevious_(e); | 444 this.selectPrevious_(e); |
| 322 e.preventDefault(); | 445 e.preventDefault(); |
| 323 break; | 446 break; |
| 324 case 39: // right arrow | 447 case 39: // right arrow |
| 325 this.selectNext_(e); | 448 this.selectNext_(e); |
| 326 e.preventDefault(); | 449 e.preventDefault(); |
| 327 break; | 450 break; |
| 328 case 9: // TAB | 451 case 9: // TAB |
| 329 if (e.shiftKey) | 452 if (this.parentElement.parentElement.tabIndex == -1) { |
| 330 this.selectPrevious_(e); | 453 if (e.shiftKey) |
| 331 else | 454 this.selectPrevious_(e); |
| 332 this.selectNext_(e); | 455 else |
| 333 e.preventDefault(); | 456 this.selectNext_(e); |
| 457 e.preventDefault(); | |
| 458 } | |
| 334 break; | 459 break; |
| 335 } | 460 } |
| 336 }, | 461 }, |
| 337 | 462 |
| 338 /** | 463 /** |
| 339 * Zoom in or out on the timeline by the given scale factor. | 464 * Zoom in or out on the timeline by the given scale factor. |
| 340 * @param {integer} scale The scale factor to apply. If <1, zooms out. | 465 * @param {integer} scale The scale factor to apply. If <1, zooms out. |
| 341 */ | 466 */ |
| 342 zoomBy_: function(scale) { | 467 zoomBy_: function(scale) { |
| 343 if (!this.firstCanvas) | 468 if (!this.firstCanvas) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 366 * @param {boolean} forwardp If true, select one forward (next). | 491 * @param {boolean} forwardp If true, select one forward (next). |
| 367 * Else, select previous. | 492 * Else, select previous. |
| 368 */ | 493 */ |
| 369 selectAdjoining_: function(e, forwardp) { | 494 selectAdjoining_: function(e, forwardp) { |
| 370 var i, track, slice, adjoining; | 495 var i, track, slice, adjoining; |
| 371 var selection = []; | 496 var selection = []; |
| 372 // Clear old selection; try and select next. | 497 // Clear old selection; try and select next. |
| 373 for (i = 0; i < this.selection_.length; ++i) { | 498 for (i = 0; i < this.selection_.length; ++i) { |
| 374 adjoining = undefined; | 499 adjoining = undefined; |
| 375 this.selection_[i].slice.selected = false; | 500 this.selection_[i].slice.selected = false; |
| 376 var track = this.selection_[i].track; | 501 track = this.selection_[i].track; |
| 377 var slice = this.selection_[i].slice; | 502 slice = this.selection_[i].slice; |
| 378 if (slice) { | 503 if (slice) { |
| 379 if (forwardp) | 504 if (forwardp) |
| 380 adjoining = track.pickNext(slice); | 505 adjoining = track.pickNext(slice); |
| 381 else | 506 else |
| 382 adjoining = track.pickPrevious(slice); | 507 adjoining = track.pickPrevious(slice); |
| 383 } | 508 } |
| 384 if (adjoining != undefined) | 509 if (adjoining != undefined) |
| 385 selection.push({track: track, slice: adjoining}); | 510 selection.push({track: track, slice: adjoining}); |
| 386 } | 511 } |
| 387 // Activate the new selection. | 512 // Activate the new selection. |
| 388 this.selection_ = selection; | 513 this.selection_ = selection; |
| 389 for (i = 0; i < this.selection_.length; ++i) | 514 for (i = 0; i < this.selection_.length; ++i) |
| 390 this.selection_[i].slice.selected = true; | 515 this.selection_[i].slice.selected = true; |
| 391 cr.dispatchSimpleEvent(this, 'selectionChange'); | 516 cr.dispatchSimpleEvent(this, 'selectionChange'); |
| 392 this.invalidate(); // Cause tracks to redraw. | 517 this.invalidate(); // Cause tracks to redraw. |
| 393 e.preventDefault(); | 518 e.preventDefault(); |
| 394 }, | 519 }, |
| 395 | 520 |
| 396 get keyHelp() { | 521 get keyHelp() { |
| 397 return 'Keyboard shortcuts:\n' + | 522 var help = 'Keyboard shortcuts:\n' + |
| 398 ' w/s : Zoom in/out (with shift: go faster)\n' + | 523 ' w/s : Zoom in/out (with shift: go faster)\n' + |
| 399 ' a/d : Pan left/right\n' + | 524 ' a/d : Pan left/right\n' + |
| 400 ' e : Center on mouse\n' + | 525 ' e : Center on mouse\n' + |
| 401 ' g/G : Shows grid at the start/end of the selected task\n' + | 526 ' g/G : Shows grid at the start/end of the selected task\n'; |
| 402 ' <-,^TAB : Select previous event on current timeline\n' + | |
| 403 ' ->, TAB : Select next event on current timeline\n' + | |
| 404 '\n' + | |
| 405 'Dbl-click to zoom in; Shift dbl-click to zoom out\n'; | |
| 406 | 527 |
| 528 if (this.parentElement.parentElement.tabIndex) | |
| 529 help += ' <- : Select previous event on current timeline\n' + | |
| 530 ' -> : Select next event on current timeline\n'; | |
| 531 else | |
| 532 help += ' <-,^TAB : Select previous event on current timeline\n' + | |
| 533 ' ->, TAB : Select next event on current timeline\n'; | |
| 407 | 534 |
| 535 help += | |
| 536 '\n' + | |
| 537 'Dbl-click to zoom in; Shift dbl-click to zoom out\n'; | |
|
jbates
2011/10/21 20:21:05
We really should add mouse scroll zoom.
| |
| 538 return help; | |
| 408 }, | 539 }, |
| 409 | 540 |
| 410 get selection() { | 541 get selection() { |
| 411 return this.selection_; | 542 return this.selection_; |
| 412 }, | 543 }, |
| 413 | 544 |
| 414 get firstCanvas() { | 545 get firstCanvas() { |
| 415 return this.tracks_.firstChild ? | 546 return this.tracks_.firstChild ? |
| 416 this.tracks_.firstChild.firstCanvas : undefined; | 547 this.tracks_.firstChild.firstCanvas : undefined; |
| 417 }, | 548 }, |
| 418 | 549 |
| 419 showDragBox_: function() { | |
| 420 this.dragBox_.hidden = false; | |
| 421 }, | |
| 422 | |
| 423 hideDragBox_: function() { | 550 hideDragBox_: function() { |
| 424 this.dragBox_.style.left = '-1000px'; | 551 this.dragBox_.style.left = '-1000px'; |
| 425 this.dragBox_.style.top = '-1000px'; | 552 this.dragBox_.style.top = '-1000px'; |
| 426 this.dragBox_.style.width = 0; | 553 this.dragBox_.style.width = 0; |
| 427 this.dragBox_.style.height = 0; | 554 this.dragBox_.style.height = 0; |
| 428 this.dragBox_.hidden = true; | |
| 429 }, | |
| 430 | |
| 431 get dragBoxVisible_() { | |
| 432 return this.dragBox_.hidden == false; | |
| 433 }, | 555 }, |
| 434 | 556 |
| 435 setDragBoxPosition_: function(eDown, eCur) { | 557 setDragBoxPosition_: function(eDown, eCur) { |
| 436 var loX = Math.min(eDown.clientX, eCur.clientX); | 558 var loX = Math.min(eDown.clientX, eCur.clientX); |
| 437 var hiX = Math.max(eDown.clientX, eCur.clientX); | 559 var hiX = Math.max(eDown.clientX, eCur.clientX); |
| 438 var loY = Math.min(eDown.clientY, eCur.clientY); | 560 var loY = Math.min(eDown.clientY, eCur.clientY); |
| 439 var hiY = Math.max(eDown.clientY, eCur.clientY); | 561 var hiY = Math.max(eDown.clientY, eCur.clientY); |
| 440 | 562 |
| 441 this.dragBox_.style.left = loX + 'px'; | 563 this.dragBox_.style.left = loX + 'px'; |
| 442 this.dragBox_.style.top = loY + 'px'; | 564 this.dragBox_.style.top = loY + 'px'; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 483 var canv = this.firstCanvas; | 605 var canv = this.firstCanvas; |
| 484 var pos = { | 606 var pos = { |
| 485 x: e.clientX - canv.offsetLeft, | 607 x: e.clientX - canv.offsetLeft, |
| 486 y: e.clientY - canv.offsetTop | 608 y: e.clientY - canv.offsetTop |
| 487 }; | 609 }; |
| 488 | 610 |
| 489 var wX = this.viewport_.xViewToWorld(pos.x); | 611 var wX = this.viewport_.xViewToWorld(pos.x); |
| 490 | 612 |
| 491 this.dragBeginEvent_ = e; | 613 this.dragBeginEvent_ = e; |
| 492 e.preventDefault(); | 614 e.preventDefault(); |
| 615 if (this.parentElement.parentElement.tabIndex) | |
| 616 this.parentElement.parentElement.focus(); | |
| 493 }, | 617 }, |
| 494 | 618 |
| 495 onMouseMove_: function(e) { | 619 onMouseMove_: function(e) { |
| 496 if (!this.firstCanvas) | 620 if (!this.firstCanvas) |
| 497 return; | 621 return; |
| 498 var canv = this.firstCanvas; | 622 var canv = this.firstCanvas; |
| 499 var pos = { | 623 var pos = { |
| 500 x: e.clientX - canv.offsetLeft, | 624 x: e.clientX - canv.offsetLeft, |
| 501 y: e.clientY - canv.offsetTop | 625 y: e.clientY - canv.offsetTop |
| 502 }; | 626 }; |
| 503 | 627 |
| 504 // Remember position. Used during keyboard zooming. | 628 // Remember position. Used during keyboard zooming. |
| 505 this.lastMouseViewPos_ = pos; | 629 this.lastMouseViewPos_ = pos; |
| 506 | 630 |
| 507 // Initiate the drag box if needed. | |
| 508 if (this.dragBeginEvent_ && !this.dragBoxVisible_) { | |
| 509 this.showDragBox_(); | |
| 510 this.setDragBoxPosition_(e, e); | |
| 511 } | |
| 512 | |
| 513 // Update the drag box | 631 // Update the drag box |
| 514 if (this.dragBeginEvent_) { | 632 if (this.dragBeginEvent_) { |
| 515 this.setDragBoxPosition_(this.dragBeginEvent_, e); | 633 this.setDragBoxPosition_(this.dragBeginEvent_, e); |
| 516 } | 634 } |
| 517 }, | 635 }, |
| 518 | 636 |
| 519 onMouseUp_: function(e) { | 637 onMouseUp_: function(e) { |
| 520 var i; | 638 var i; |
| 521 if (this.dragBeginEvent_) { | 639 if (this.dragBeginEvent_) { |
| 522 // Stop the dragging. | 640 // Stop the dragging. |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 565 this.invalidate(); // Cause tracks to redraw. | 683 this.invalidate(); // Cause tracks to redraw. |
| 566 } | 684 } |
| 567 }, | 685 }, |
| 568 | 686 |
| 569 onDblClick_: function(e) { | 687 onDblClick_: function(e) { |
| 570 var scale = 4; | 688 var scale = 4; |
| 571 if (e.shiftKey) | 689 if (e.shiftKey) |
| 572 scale = 1 / scale; | 690 scale = 1 / scale; |
| 573 this.zoomBy_(scale); | 691 this.zoomBy_(scale); |
| 574 e.preventDefault(); | 692 e.preventDefault(); |
| 575 }, | 693 } |
| 576 }; | 694 }; |
| 577 | 695 |
| 578 /** | 696 /** |
| 579 * The TimelineModel being viewed by the timeline | 697 * The TimelineModel being viewed by the timeline |
| 580 * @type {TimelineModel} | 698 * @type {TimelineModel} |
| 581 */ | 699 */ |
| 582 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); | 700 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); |
| 583 | 701 |
| 584 return { | 702 return { |
| 585 Timeline: Timeline | 703 Timeline: Timeline |
| 586 }; | 704 }; |
| 587 }); | 705 }); |
| OLD | NEW |