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 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 160 | 160 |
| 161 this.invalidatePending_ = false; | 161 this.invalidatePending_ = false; |
| 162 | 162 |
| 163 this.tracks_ = this.ownerDocument.createElement('div'); | 163 this.tracks_ = this.ownerDocument.createElement('div'); |
| 164 this.tracks_.invalidate = this.invalidate.bind(this); | 164 this.tracks_.invalidate = this.invalidate.bind(this); |
| 165 this.appendChild(this.tracks_); | 165 this.appendChild(this.tracks_); |
| 166 | 166 |
| 167 this.dragBox_ = this.ownerDocument.createElement('div'); | 167 this.dragBox_ = this.ownerDocument.createElement('div'); |
| 168 this.dragBox_.className = 'timeline-drag-box'; | 168 this.dragBox_.className = 'timeline-drag-box'; |
| 169 this.appendChild(this.dragBox_); | 169 this.appendChild(this.dragBox_); |
| 170 this.hideDragBox_(); | |
| 170 | 171 |
| 171 // The following code uses a setInterval to monitor the timeline control | 172 // 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 | 173 // for size changes. This is so that we can keep the canvas' bitmap size |
| 173 // correctly synchronized with its presentation size. | 174 // correctly synchronized with its presentation size. |
| 174 // TODO(nduca): detect this in a more efficient way, e.g. iframe hack. | 175 // TODO(nduca): detect this in a more efficient way, e.g. iframe hack. |
| 175 this.lastSize_ = this.clientWidth + 'x' + this.clientHeight; | 176 this.lastSize_ = this.clientWidth + 'x' + this.clientHeight; |
| 176 this.ownerDocument.defaultView.setInterval(function() { | 177 this.checkForResizeInterval_ = |
| 178 this.ownerDocument.defaultView.setInterval(function() { | |
| 179 if (!this.isAttachedToDocument_) | |
| 180 return; | |
| 177 var curSize = this.clientWidth + 'x' + this.clientHeight; | 181 var curSize = this.clientWidth + 'x' + this.clientHeight; |
| 178 if (this.clientWidth && curSize != this.lastSize_) { | 182 if (this.clientWidth && curSize != this.lastSize_) { |
| 179 this.lastSize_ = curSize; | 183 this.lastSize_ = curSize; |
| 180 this.onResize(); | 184 this.onResize(); |
| 181 } | 185 } |
| 182 }.bind(this), 250); | 186 }.bind(this), 250); |
| 183 | 187 |
| 184 document.addEventListener('keypress', this.onKeypress_.bind(this)); | 188 this.bindEventListener_(document, 'keypress', this.onKeypress_, this); |
| 185 document.addEventListener('keydown', this.onKeydown_.bind(this)); | 189 this.bindEventListener_(document, 'keydown', this.onKeydown_, this); |
| 186 document.addEventListener('mousedown', this.onMouseDown_.bind(this)); | 190 this.bindEventListener_(document, 'mousedown', this.onMouseDown_, this); |
| 187 document.addEventListener('mousemove', this.onMouseMove_.bind(this)); | 191 this.bindEventListener_(document, 'mousemove', this.onMouseMove_, this); |
| 188 document.addEventListener('mouseup', this.onMouseUp_.bind(this)); | 192 this.bindEventListener_(document, 'mouseup', this.onMouseUp_, this); |
| 189 document.addEventListener('dblclick', this.onDblClick_.bind(this)); | 193 this.bindEventListener_(document, 'dblclick', this.onDblClick_, this); |
| 190 | 194 |
| 191 this.lastMouseViewPos_ = {x: 0, y: 0}; | 195 this.lastMouseViewPos_ = {x: 0, y: 0}; |
| 192 | 196 |
| 193 this.selection_ = []; | 197 this.selection_ = []; |
| 194 }, | 198 }, |
| 195 | 199 |
| 200 /** | |
| 201 * Wraps the stanard addEventListener but automatically binds the provided | |
|
James Hawkins
2011/11/10 01:06:13
s/stanard/standard/
nduca
2011/11/10 02:24:35
Done.
| |
| 202 * func to the provided target, tracking the resulting closure. When detach | |
| 203 * is called, these listeners will be automatically removed. | |
| 204 */ | |
| 205 bindEventListener_: function(object, event, func, target) { | |
| 206 if (!this.boundListeners_) | |
| 207 this.boundListeners_ = []; | |
| 208 var boundFunc = func.bind(target); | |
| 209 this.boundListeners_.push({object: object, | |
| 210 event: event, | |
| 211 boundFunc: boundFunc}); | |
| 212 object.addEventListener(event, boundFunc); | |
| 213 }, | |
| 214 | |
| 215 detach: function() { | |
| 216 for (var i = 0; i < this.boundListeners_.length; ++i) { | |
| 217 var binding = this.boundListeners_[i]; | |
| 218 binding.object.removeEventListener(binding.event, binding.boundFunc); | |
| 219 } | |
| 220 this.boundListeners_ = undefined; | |
| 221 window.clearInterval(this.checkForResizeInterval_); | |
| 222 this.checkForResizeInterval_ = undefined; | |
| 223 }, | |
| 224 | |
| 196 get model() { | 225 get model() { |
| 197 return this.model_; | 226 return this.model_; |
| 198 }, | 227 }, |
| 199 | 228 |
| 200 set model(model) { | 229 set model(model) { |
| 201 if (!model) | 230 if (!model) |
| 202 throw Error('Model cannot be null'); | 231 throw Error('Model cannot be null'); |
| 203 if (this.model) { | 232 if (this.model) { |
| 204 throw Error('Cannot set model twice.'); | 233 throw Error('Cannot set model twice.'); |
| 205 } | 234 } |
| 206 this.model_ = model; | 235 this.model_ = model; |
| 207 | 236 |
| 208 // Create tracks. | 237 // Create tracks and measure their heading size. |
| 209 this.tracks_.textContent = ''; | |
| 210 var threads = model.getAllThreads(); | 238 var threads = model.getAllThreads(); |
| 211 threads.sort(tracing.TimelineThread.compare); | 239 var maxHeadingWidth = 0; |
| 240 var tracks = []; | |
| 241 var measuringStick = new tracing.MeasuringStick(); | |
| 242 var headingEl = document.createElement('div'); | |
| 243 headingEl.style.position = 'fixed'; | |
| 244 headingEl.className = 'timeline-slice-track-title'; | |
| 212 for (var tI = 0; tI < threads.length; tI++) { | 245 for (var tI = 0; tI < threads.length; tI++) { |
| 213 var thread = threads[tI]; | 246 var thread = threads[tI]; |
| 214 var track = new TimelineThreadTrack(); | 247 var track = new TimelineThreadTrack(); |
| 215 track.thread = thread; | 248 track.thread = thread; |
| 216 track.viewport = this.viewport_; | 249 track.viewport = this.viewport_; |
| 250 tracks.push(track); | |
| 251 headingEl.textContent = track.heading; | |
| 252 var w = measuringStick.measure(headingEl).width; | |
| 253 // Limit heading width to 300px. | |
| 254 if (w > 300) | |
| 255 w = 300; | |
| 256 if (w > maxHeadingWidth) | |
| 257 maxHeadingWidth = w; | |
| 258 } | |
| 259 maxHeadingWidth += maxHeadingWidth + 4; | |
|
James Hawkins
2011/11/10 01:06:13
What is the 4 about?
nduca
2011/11/10 02:24:35
Its some extra padding to make things look less cr
| |
| 260 | |
| 261 // Attach tracks and set width. | |
| 262 this.tracks_.textContent = ''; | |
| 263 threads.sort(tracing.TimelineThread.compare); | |
| 264 for (var tI = 0; tI < tracks.length; tI++) { | |
| 265 var track = tracks[tI]; | |
| 266 track.headingWidth = maxHeadingWidthWithUnit + 'px'; | |
| 217 this.tracks_.appendChild(track); | 267 this.tracks_.appendChild(track); |
| 218 | |
| 219 } | 268 } |
| 220 | 269 |
| 221 this.needsViewportReset_ = true; | 270 if (this.isAttachedToDocument_) { |
|
James Hawkins
2011/11/10 01:06:13
nit: No braces for single-line blocks.
nduca
2011/11/10 02:24:35
Done.
| |
| 271 this.onResize(); | |
| 272 } else { | |
| 273 this.needsViewportReset_ = true; | |
| 274 } | |
| 222 }, | 275 }, |
| 223 | 276 |
| 224 viewportChange_: function() { | 277 viewportChange_: function() { |
| 225 this.invalidate(); | 278 this.invalidate(); |
| 226 }, | 279 }, |
| 227 | 280 |
| 228 invalidate: function() { | 281 invalidate: function() { |
| 229 if (this.invalidatePending_) | 282 if (this.invalidatePending_) |
| 230 return; | 283 return; |
| 231 this.invalidatePending_ = true; | 284 this.invalidatePending_ = true; |
| 232 window.setTimeout(function() { | 285 if (this.isAttachedToDocument_) |
| 233 this.invalidatePending_ = false; | 286 window.setTimeout(function() { |
| 234 this.redrawAllTracks_(); | 287 this.invalidatePending_ = false; |
| 235 }.bind(this), 0); | 288 this.redrawAllTracks_(); |
| 289 }.bind(this), 0); | |
| 290 }, | |
| 291 | |
| 292 /** | |
| 293 * @return {boolean} Whether the current timeline is attached to the | |
| 294 * document. | |
| 295 */ | |
| 296 get isAttachedToDocument_() { | |
| 297 var cur = this; | |
| 298 while (cur.parentNode) | |
| 299 cur = cur.parentNode; | |
| 300 return cur == this.ownerDocument; | |
| 236 }, | 301 }, |
| 237 | 302 |
| 238 onResize: function() { | 303 onResize: function() { |
| 304 if (!this.isAttachedToDocument_) | |
| 305 throw 'Not attached to document!'; | |
| 239 for (var i = 0; i < this.tracks_.children.length; ++i) { | 306 for (var i = 0; i < this.tracks_.children.length; ++i) { |
| 240 var track = this.tracks_.children[i]; | 307 var track = this.tracks_.children[i]; |
| 241 track.onResize(); | 308 track.onResize(); |
| 242 } | 309 } |
| 310 if (this.invalidatePending_) { | |
| 311 this.invalidatePending_ = false; | |
| 312 this.redrawAllTracks_(); | |
| 313 } | |
| 243 }, | 314 }, |
| 244 | 315 |
| 245 redrawAllTracks_: function() { | 316 redrawAllTracks_: function() { |
| 246 if (this.needsViewportReset_ && this.clientWidth != 0) { | 317 if (this.needsViewportReset_ && this.clientWidth != 0) { |
| 318 if (!this.isAttachedToDocument_) | |
| 319 throw 'Not attached to document!'; | |
| 247 this.needsViewportReset_ = false; | 320 this.needsViewportReset_ = false; |
| 248 /* update viewport */ | 321 /* update viewport */ |
| 249 var rangeTimestamp = this.model_.maxTimestamp - | 322 var rangeTimestamp = this.model_.maxTimestamp - |
| 250 this.model_.minTimestamp; | 323 this.model_.minTimestamp; |
| 251 var w = this.firstCanvas.width; | 324 var w = this.firstCanvas.width; |
| 252 console.log('viewport was reset with w=', w); | |
| 253 var scaleX = w / rangeTimestamp; | 325 var scaleX = w / rangeTimestamp; |
| 254 var panX = -this.model_.minTimestamp; | 326 var panX = -this.model_.minTimestamp; |
| 255 this.viewport_.setPanAndScale(panX, scaleX); | 327 this.viewport_.setPanAndScale(panX, scaleX); |
| 256 } | 328 } |
| 257 for (var i = 0; i < this.tracks_.children.length; ++i) { | 329 for (var i = 0; i < this.tracks_.children.length; ++i) { |
| 258 this.tracks_.children[i].redraw(); | 330 this.tracks_.children[i].redraw(); |
| 259 } | 331 } |
| 260 }, | 332 }, |
| 261 | 333 |
| 262 updateChildViewports_: function() { | 334 updateChildViewports_: function() { |
| 263 for (var cI = 0; cI < this.tracks_.children.length; ++cI) { | 335 for (var cI = 0; cI < this.tracks_.children.length; ++cI) { |
| 264 var child = this.tracks_.children[cI]; | 336 var child = this.tracks_.children[cI]; |
| 265 child.setViewport(this.panX, this.scaleX); | 337 child.setViewport(this.panX, this.scaleX); |
| 266 } | 338 } |
| 267 }, | 339 }, |
| 268 | 340 |
| 341 get listenToKeys_() { | |
| 342 if (this.parentElement.parentElement.tabIndex >= 0) | |
| 343 return document.activeElement == this.parentElement.parentElement; | |
| 344 return true; | |
| 345 }, | |
| 346 | |
| 269 onKeypress_: function(e) { | 347 onKeypress_: function(e) { |
| 270 var vp = this.viewport_; | 348 var vp = this.viewport_; |
| 271 if (!this.firstCanvas) | 349 if (!this.firstCanvas) |
| 272 return; | 350 return; |
| 351 if (!this.listenToKeys_) | |
| 352 return; | |
| 273 var viewWidth = this.firstCanvas.clientWidth; | 353 var viewWidth = this.firstCanvas.clientWidth; |
| 274 var curMouseV, curCenterW; | 354 var curMouseV, curCenterW; |
| 275 switch (e.keyCode) { | 355 switch (e.keyCode) { |
| 276 case 101: // e | 356 case 101: // e |
| 277 var vX = this.lastMouseViewPos_.x; | 357 var vX = this.lastMouseViewPos_.x; |
| 278 var wX = vp.xViewToWorld(this.lastMouseViewPos_.x); | 358 var wX = vp.xViewToWorld(this.lastMouseViewPos_.x); |
| 279 var distFromCenter = vX - (viewWidth / 2); | 359 var distFromCenter = vX - (viewWidth / 2); |
| 280 var percFromCenter = distFromCenter / viewWidth; | 360 var percFromCenter = distFromCenter / viewWidth; |
| 281 var percFromCenterSq = percFromCenter * percFromCenter; | 361 var percFromCenterSq = percFromCenter * percFromCenter; |
| 282 vp.xPanWorldPosToViewPos(wX, 'center', viewWidth); | 362 vp.xPanWorldPosToViewPos(wX, 'center', viewWidth); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 309 vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5); | 389 vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5); |
| 310 break; | 390 break; |
| 311 case 68: // D | 391 case 68: // D |
| 312 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); | 392 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); |
| 313 break; | 393 break; |
| 314 } | 394 } |
| 315 }, | 395 }, |
| 316 | 396 |
| 317 // Not all keys send a keypress. | 397 // Not all keys send a keypress. |
| 318 onKeydown_: function(e) { | 398 onKeydown_: function(e) { |
| 399 if (!this.listenToKeys_) | |
| 400 return; | |
| 319 switch (e.keyCode) { | 401 switch (e.keyCode) { |
| 320 case 37: // left arrow | 402 case 37: // left arrow |
| 321 this.selectPrevious_(e); | 403 this.selectPrevious_(e); |
| 322 e.preventDefault(); | 404 e.preventDefault(); |
| 323 break; | 405 break; |
| 324 case 39: // right arrow | 406 case 39: // right arrow |
| 325 this.selectNext_(e); | 407 this.selectNext_(e); |
| 326 e.preventDefault(); | 408 e.preventDefault(); |
| 327 break; | 409 break; |
| 328 case 9: // TAB | 410 case 9: // TAB |
| 329 if (e.shiftKey) | 411 if (this.parentElement.parentElement.tabIndex == -1) { |
| 330 this.selectPrevious_(e); | 412 if (e.shiftKey) |
| 331 else | 413 this.selectPrevious_(e); |
| 332 this.selectNext_(e); | 414 else |
| 333 e.preventDefault(); | 415 this.selectNext_(e); |
| 416 e.preventDefault(); | |
| 417 } | |
| 334 break; | 418 break; |
| 335 } | 419 } |
| 336 }, | 420 }, |
| 337 | 421 |
| 338 /** | 422 /** |
| 339 * Zoom in or out on the timeline by the given scale factor. | 423 * 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. | 424 * @param {integer} scale The scale factor to apply. If <1, zooms out. |
| 341 */ | 425 */ |
| 342 zoomBy_: function(scale) { | 426 zoomBy_: function(scale) { |
| 343 if (!this.firstCanvas) | 427 if (!this.firstCanvas) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 366 * @param {boolean} forwardp If true, select one forward (next). | 450 * @param {boolean} forwardp If true, select one forward (next). |
| 367 * Else, select previous. | 451 * Else, select previous. |
| 368 */ | 452 */ |
| 369 selectAdjoining_: function(e, forwardp) { | 453 selectAdjoining_: function(e, forwardp) { |
| 370 var i, track, slice, adjoining; | 454 var i, track, slice, adjoining; |
| 371 var selection = []; | 455 var selection = []; |
| 372 // Clear old selection; try and select next. | 456 // Clear old selection; try and select next. |
| 373 for (i = 0; i < this.selection_.length; ++i) { | 457 for (i = 0; i < this.selection_.length; ++i) { |
| 374 adjoining = undefined; | 458 adjoining = undefined; |
| 375 this.selection_[i].slice.selected = false; | 459 this.selection_[i].slice.selected = false; |
| 376 var track = this.selection_[i].track; | 460 track = this.selection_[i].track; |
| 377 var slice = this.selection_[i].slice; | 461 slice = this.selection_[i].slice; |
| 378 if (slice) { | 462 if (slice) { |
| 379 if (forwardp) | 463 if (forwardp) |
| 380 adjoining = track.pickNext(slice); | 464 adjoining = track.pickNext(slice); |
| 381 else | 465 else |
| 382 adjoining = track.pickPrevious(slice); | 466 adjoining = track.pickPrevious(slice); |
| 383 } | 467 } |
| 384 if (adjoining != undefined) | 468 if (adjoining != undefined) |
| 385 selection.push({track: track, slice: adjoining}); | 469 selection.push({track: track, slice: adjoining}); |
| 386 } | 470 } |
| 387 // Activate the new selection. | 471 // Activate the new selection. |
| 388 this.selection_ = selection; | 472 this.selection_ = selection; |
| 389 for (i = 0; i < this.selection_.length; ++i) | 473 for (i = 0; i < this.selection_.length; ++i) |
| 390 this.selection_[i].slice.selected = true; | 474 this.selection_[i].slice.selected = true; |
| 391 cr.dispatchSimpleEvent(this, 'selectionChange'); | 475 cr.dispatchSimpleEvent(this, 'selectionChange'); |
| 392 this.invalidate(); // Cause tracks to redraw. | 476 this.invalidate(); // Cause tracks to redraw. |
| 393 e.preventDefault(); | 477 e.preventDefault(); |
| 394 }, | 478 }, |
| 395 | 479 |
| 396 get keyHelp() { | 480 get keyHelp() { |
| 397 return 'Keyboard shortcuts:\n' + | 481 var help = 'Keyboard shortcuts:\n' + |
| 398 ' w/s : Zoom in/out (with shift: go faster)\n' + | 482 ' w/s : Zoom in/out (with shift: go faster)\n' + |
| 399 ' a/d : Pan left/right\n' + | 483 ' a/d : Pan left/right\n' + |
| 400 ' e : Center on mouse\n' + | 484 ' e : Center on mouse\n' + |
| 401 ' g/G : Shows grid at the start/end of the selected task\n' + | 485 ' 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 | 486 |
| 487 if (this.parentElement.parentElement.tabIndex) | |
|
James Hawkins
2011/11/10 01:06:13
nit: Multi-line blocks must have braces.
nduca
2011/11/10 02:24:35
Done.
| |
| 488 help += ' <- : Select previous event on current timeline\n' + | |
| 489 ' -> : Select next event on current timeline\n'; | |
| 490 else | |
| 491 help += ' <-,^TAB : Select previous event on current timeline\n' + | |
| 492 ' ->, TAB : Select next event on current timeline\n'; | |
| 407 | 493 |
| 494 help += | |
| 495 '\n' + | |
| 496 'Dbl-click to zoom in; Shift dbl-click to zoom out\n'; | |
| 497 return help; | |
| 408 }, | 498 }, |
| 409 | 499 |
| 410 get selection() { | 500 get selection() { |
| 411 return this.selection_; | 501 return this.selection_; |
| 412 }, | 502 }, |
| 413 | 503 |
| 504 set selection(var sel) { | |
| 505 // Clear old selection. | |
| 506 for (i = 0; i < this.selection_.length; ++i) { | |
|
James Hawkins
2011/11/10 01:06:13
nit: No braces for single-line blocks.
nduca
2011/11/10 02:24:35
Done.
| |
| 507 this.selection_[i].slice.selected = false; | |
| 508 } | |
| 509 | |
| 510 this.selection_ = sel; | |
| 511 | |
| 512 this.selection_ = selection; | |
| 513 cr.dispatchSimpleEvent(this, 'selectionChange'); | |
| 514 for (i = 0; i < this.selection_.length; ++i) { | |
|
James Hawkins
2011/11/10 01:06:13
nit: No braces for single-line blocks.
nduca
2011/11/10 02:24:35
Done.
| |
| 515 this.selection_[i].slice.selected = true; | |
| 516 } | |
| 517 this.invalidate(); // Cause tracks to redraw. | |
| 518 } | |
| 519 | |
| 414 get firstCanvas() { | 520 get firstCanvas() { |
| 415 return this.tracks_.firstChild ? | 521 return this.tracks_.firstChild ? |
| 416 this.tracks_.firstChild.firstCanvas : undefined; | 522 this.tracks_.firstChild.firstCanvas : undefined; |
| 417 }, | 523 }, |
| 418 | 524 |
| 419 showDragBox_: function() { | |
| 420 this.dragBox_.hidden = false; | |
| 421 }, | |
| 422 | |
| 423 hideDragBox_: function() { | 525 hideDragBox_: function() { |
| 424 this.dragBox_.style.left = '-1000px'; | 526 this.dragBox_.style.left = '-1000px'; |
| 425 this.dragBox_.style.top = '-1000px'; | 527 this.dragBox_.style.top = '-1000px'; |
| 426 this.dragBox_.style.width = 0; | 528 this.dragBox_.style.width = 0; |
| 427 this.dragBox_.style.height = 0; | 529 this.dragBox_.style.height = 0; |
| 428 this.dragBox_.hidden = true; | |
| 429 }, | |
| 430 | |
| 431 get dragBoxVisible_() { | |
| 432 return this.dragBox_.hidden == false; | |
| 433 }, | 530 }, |
| 434 | 531 |
| 435 setDragBoxPosition_: function(eDown, eCur) { | 532 setDragBoxPosition_: function(eDown, eCur) { |
| 436 var loX = Math.min(eDown.clientX, eCur.clientX); | 533 var loX = Math.min(eDown.clientX, eCur.clientX); |
| 437 var hiX = Math.max(eDown.clientX, eCur.clientX); | 534 var hiX = Math.max(eDown.clientX, eCur.clientX); |
| 438 var loY = Math.min(eDown.clientY, eCur.clientY); | 535 var loY = Math.min(eDown.clientY, eCur.clientY); |
| 439 var hiY = Math.max(eDown.clientY, eCur.clientY); | 536 var hiY = Math.max(eDown.clientY, eCur.clientY); |
| 440 | 537 |
| 441 this.dragBox_.style.left = loX + 'px'; | 538 this.dragBox_.style.left = loX + 'px'; |
| 442 this.dragBox_.style.top = loY + 'px'; | 539 this.dragBox_.style.top = loY + 'px'; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 483 var canv = this.firstCanvas; | 580 var canv = this.firstCanvas; |
| 484 var pos = { | 581 var pos = { |
| 485 x: e.clientX - canv.offsetLeft, | 582 x: e.clientX - canv.offsetLeft, |
| 486 y: e.clientY - canv.offsetTop | 583 y: e.clientY - canv.offsetTop |
| 487 }; | 584 }; |
| 488 | 585 |
| 489 var wX = this.viewport_.xViewToWorld(pos.x); | 586 var wX = this.viewport_.xViewToWorld(pos.x); |
| 490 | 587 |
| 491 this.dragBeginEvent_ = e; | 588 this.dragBeginEvent_ = e; |
| 492 e.preventDefault(); | 589 e.preventDefault(); |
| 590 if (this.parentElement.parentElement.tabIndex) | |
| 591 this.parentElement.parentElement.focus(); | |
| 493 }, | 592 }, |
| 494 | 593 |
| 495 onMouseMove_: function(e) { | 594 onMouseMove_: function(e) { |
| 496 if (!this.firstCanvas) | 595 if (!this.firstCanvas) |
| 497 return; | 596 return; |
| 498 var canv = this.firstCanvas; | 597 var canv = this.firstCanvas; |
| 499 var pos = { | 598 var pos = { |
| 500 x: e.clientX - canv.offsetLeft, | 599 x: e.clientX - canv.offsetLeft, |
| 501 y: e.clientY - canv.offsetTop | 600 y: e.clientY - canv.offsetTop |
| 502 }; | 601 }; |
| 503 | 602 |
| 504 // Remember position. Used during keyboard zooming. | 603 // Remember position. Used during keyboard zooming. |
| 505 this.lastMouseViewPos_ = pos; | 604 this.lastMouseViewPos_ = pos; |
| 506 | 605 |
| 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 | 606 // Update the drag box |
| 514 if (this.dragBeginEvent_) { | 607 if (this.dragBeginEvent_) { |
| 515 this.setDragBoxPosition_(this.dragBeginEvent_, e); | 608 this.setDragBoxPosition_(this.dragBeginEvent_, e); |
| 516 } | 609 } |
| 517 }, | 610 }, |
| 518 | 611 |
| 519 onMouseUp_: function(e) { | 612 onMouseUp_: function(e) { |
| 520 var i; | 613 var i; |
| 521 if (this.dragBeginEvent_) { | 614 if (this.dragBeginEvent_) { |
| 522 // Stop the dragging. | 615 // Stop the dragging. |
| 523 this.hideDragBox_(); | 616 this.hideDragBox_(); |
| 524 var eDown = this.dragBeginEvent_; | 617 var eDown = this.dragBeginEvent_; |
| 525 this.dragBeginEvent_ = null; | 618 this.dragBeginEvent_ = null; |
| 526 | 619 |
| 527 // Figure out extents of the drag. | 620 // Figure out extents of the drag. |
| 528 var loX = Math.min(eDown.clientX, e.clientX); | 621 var loX = Math.min(eDown.clientX, e.clientX); |
| 529 var hiX = Math.max(eDown.clientX, e.clientX); | 622 var hiX = Math.max(eDown.clientX, e.clientX); |
| 530 var loY = Math.min(eDown.clientY, e.clientY); | 623 var loY = Math.min(eDown.clientY, e.clientY); |
| 531 var hiY = Math.max(eDown.clientY, e.clientY); | 624 var hiY = Math.max(eDown.clientY, e.clientY); |
| 532 | 625 |
| 533 // Convert to worldspace. | 626 // Convert to worldspace. |
| 534 var canv = this.firstCanvas; | 627 var canv = this.firstCanvas; |
| 535 var loWX = this.viewport_.xViewToWorld(loX - canv.offsetLeft); | 628 var loWX = this.viewport_.xViewToWorld(loX - canv.offsetLeft); |
| 536 var hiWX = this.viewport_.xViewToWorld(hiX - canv.offsetLeft); | 629 var hiWX = this.viewport_.xViewToWorld(hiX - canv.offsetLeft); |
| 537 | 630 |
| 538 // Clear old selection. | |
| 539 for (i = 0; i < this.selection_.length; ++i) { | |
| 540 this.selection_[i].slice.selected = false; | |
| 541 } | |
| 542 | |
| 543 // Figure out what has been hit. | 631 // Figure out what has been hit. |
| 544 var selection = []; | 632 var selection = []; |
| 545 function addHit(type, track, slice) { | 633 function addHit(type, track, slice) { |
| 546 selection.push({track: track, slice: slice}); | 634 selection.push({track: track, slice: slice}); |
| 547 } | 635 } |
| 548 for (i = 0; i < this.tracks_.children.length; ++i) { | 636 for (i = 0; i < this.tracks_.children.length; ++i) { |
| 549 var track = this.tracks_.children[i]; | 637 var track = this.tracks_.children[i]; |
| 550 | 638 |
| 551 // Only check tracks that insersect the rect. | 639 // Only check tracks that insersect the rect. |
| 552 var trackClientRect = track.getBoundingClientRect(); | 640 var trackClientRect = track.getBoundingClientRect(); |
| 553 var a = Math.max(loY, trackClientRect.top); | 641 var a = Math.max(loY, trackClientRect.top); |
| 554 var b = Math.min(hiY, trackClientRect.bottom); | 642 var b = Math.min(hiY, trackClientRect.bottom); |
| 555 if (a <= b) { | 643 if (a <= b) { |
| 556 track.pickRange(loWX, hiWX, loY, hiY, addHit); | 644 track.pickRange(loWX, hiWX, loY, hiY, addHit); |
| 557 } | 645 } |
| 558 } | 646 } |
| 559 // Activate the new selection. | 647 // Activate the new selection. |
| 560 this.selection_ = selection; | 648 this.selection = selection; |
| 561 cr.dispatchSimpleEvent(this, 'selectionChange'); | |
| 562 for (i = 0; i < this.selection_.length; ++i) { | |
| 563 this.selection_[i].slice.selected = true; | |
| 564 } | |
| 565 this.invalidate(); // Cause tracks to redraw. | |
| 566 } | 649 } |
| 567 }, | 650 }, |
| 568 | 651 |
| 569 onDblClick_: function(e) { | 652 onDblClick_: function(e) { |
| 570 var scale = 4; | 653 var scale = 4; |
| 571 if (e.shiftKey) | 654 if (e.shiftKey) |
| 572 scale = 1 / scale; | 655 scale = 1 / scale; |
| 573 this.zoomBy_(scale); | 656 this.zoomBy_(scale); |
| 574 e.preventDefault(); | 657 e.preventDefault(); |
| 575 }, | 658 }, |
| 576 }; | 659 }; |
| 577 | 660 |
| 578 /** | 661 /** |
| 579 * The TimelineModel being viewed by the timeline | 662 * The TimelineModel being viewed by the timeline |
| 580 * @type {TimelineModel} | 663 * @type {TimelineModel} |
| 581 */ | 664 */ |
| 582 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); | 665 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); |
| 583 | 666 |
| 584 return { | 667 return { |
| 585 Timeline: Timeline | 668 Timeline: Timeline |
| 586 }; | 669 }; |
| 587 }); | 670 }); |
| OLD | NEW |