OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * @fileoverview Interactive visualizaiton of TimelineModel objects | 8 * @fileoverview Interactive visualizaiton of TimelineModel objects |
9 * based loosely on gantt charts. Each thread in the TimelineModel is given a | 9 * based loosely on gantt charts. Each thread in the TimelineModel is given a |
10 * set of TimelineTracks, one per subrow in the thread. The Timeline class | 10 * set of TimelineTracks, one per subrow in the thread. The Timeline class |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 | 225 |
226 get gridStep() { | 226 get gridStep() { |
227 return this.gridStep_; | 227 return this.gridStep_; |
228 }, | 228 }, |
229 | 229 |
230 applyTransformToCanavs: function(ctx) { | 230 applyTransformToCanavs: function(ctx) { |
231 ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0); | 231 ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0); |
232 } | 232 } |
233 }; | 233 }; |
234 | 234 |
| 235 function TimelineSelectionSliceHit(track, slice) { |
| 236 this.track = track; |
| 237 this.slice = slice; |
| 238 } |
| 239 TimelineSelectionSliceHit.prototype = { |
| 240 get selected() { |
| 241 return this.slice.selected; |
| 242 }, |
| 243 set selected(v) { |
| 244 this.slice.selected = v; |
| 245 } |
| 246 }; |
| 247 |
| 248 function TimelineSelectionCounterSampleHit(track, counter, sampleIndex) { |
| 249 this.track = track; |
| 250 this.counter = counter; |
| 251 this.sampleIndex = sampleIndex; |
| 252 } |
| 253 TimelineSelectionCounterSampleHit.prototype = { |
| 254 get selected() { |
| 255 return this.track.selectedSamples[this.sampleIndex] == true; |
| 256 }, |
| 257 set selected(v) { |
| 258 if (v) |
| 259 this.track.selectedSamples[this.sampleIndex] = true; |
| 260 else |
| 261 this.track.selectedSamples[this.sampleIndex] = false; |
| 262 this.track.invalidate(); |
| 263 } |
| 264 }; |
| 265 |
| 266 |
| 267 /** |
| 268 * Represents a selection within a Timeline and its associated set of tracks. |
| 269 * @constructor |
| 270 */ |
| 271 function TimelineSelection() { |
| 272 this.range_dirty_ = true; |
| 273 this.range_ = {}; |
| 274 this.length_ = 0; |
| 275 } |
| 276 TimelineSelection.prototype = { |
| 277 __proto__: Object.prototype, |
| 278 |
| 279 get range() { |
| 280 if (this.range_dirty_) { |
| 281 var wmin = Infinity; |
| 282 var wmax = -wmin; |
| 283 for (var i = 0; i < this.length_; i++) { |
| 284 var hit = this[i]; |
| 285 if (hit.slice) { |
| 286 wmin = Math.min(wmin, hit.slice.start); |
| 287 wmax = Math.max(wmax, hit.slice.end); |
| 288 } |
| 289 } |
| 290 this.range_ = { |
| 291 min: wmin, |
| 292 max: wmax |
| 293 }; |
| 294 this.range_dirty_ = false; |
| 295 } |
| 296 return this.range_; |
| 297 }, |
| 298 |
| 299 get duration() { |
| 300 return this.range.max - this.range.min; |
| 301 }, |
| 302 |
| 303 get length() { |
| 304 return this.length_; |
| 305 }, |
| 306 |
| 307 clear: function() { |
| 308 for (var i = 0; i < this.length_; ++i) |
| 309 delete this[i]; |
| 310 this.length_ = 0; |
| 311 this.range_dirty_ = true; |
| 312 }, |
| 313 |
| 314 push_: function(hit) { |
| 315 this[this.length_++] = hit; |
| 316 this.range_dirty_ = true; |
| 317 return hit; |
| 318 }, |
| 319 |
| 320 addSlice: function(track, slice) { |
| 321 return this.push_(new TimelineSelectionSliceHit(track, slice)); |
| 322 }, |
| 323 |
| 324 addCounterSample: function(track, counter, sampleIndex) { |
| 325 return this.push_( |
| 326 new TimelineSelectionCounterSampleHit( |
| 327 track, counter, sampleIndex)); |
| 328 }, |
| 329 |
| 330 subSelection: function(index, count) { |
| 331 count = count || 1; |
| 332 |
| 333 var selection = new TimelineSelection(); |
| 334 selection.range_dirty_ = true; |
| 335 if (index < 0 || index + count > this.length_) |
| 336 throw 'Index out of bounds'; |
| 337 |
| 338 for (var i = index; i < index + count; i++) |
| 339 selection.push_(this[i]); |
| 340 |
| 341 return selection; |
| 342 }, |
| 343 |
| 344 getCounterSampleHits: function() { |
| 345 var selection = new TimelineSelection(); |
| 346 for (var i = 0; i < this.length_; i++) |
| 347 if (this[i] instanceof TimelineSelectionCounterSampleHit) |
| 348 selection.push_(this[i]); |
| 349 return selection; |
| 350 }, |
| 351 |
| 352 getSliceHits: function() { |
| 353 var selection = new TimelineSelection(); |
| 354 for (var i = 0; i < this.length_; i++) |
| 355 if (this[i] instanceof TimelineSelectionSliceHit) |
| 356 selection.push_(this[i]); |
| 357 return selection; |
| 358 }, |
| 359 |
| 360 map: function(fn) { |
| 361 for (var i = 0; i < this.length_; i++) |
| 362 fn(this[i]); |
| 363 }, |
| 364 |
| 365 /** |
| 366 * Helper for selection previous or next. |
| 367 * @param {boolean} forwardp If true, select one forward (next). |
| 368 * Else, select previous. |
| 369 * @return {boolean} true if current selection changed. |
| 370 */ |
| 371 getShiftedSelection: function(offset) { |
| 372 var newSelection = new TimelineSelection(); |
| 373 for (var i = 0; i < this.length_; i++) { |
| 374 var hit = this[i]; |
| 375 hit.track.addItemNearToProvidedHitToSelection( |
| 376 hit, offset, newSelection); |
| 377 } |
| 378 |
| 379 if (newSelection.length == 0) |
| 380 return undefined; |
| 381 return newSelection; |
| 382 }, |
| 383 }; |
| 384 |
235 /** | 385 /** |
236 * Renders a TimelineModel into a div element, making one | 386 * Renders a TimelineModel into a div element, making one |
237 * TimelineTrack for each subrow in each thread of the model, managing | 387 * TimelineTrack for each subrow in each thread of the model, managing |
238 * overall track layout, and handling user interaction with the | 388 * overall track layout, and handling user interaction with the |
239 * viewport. | 389 * viewport. |
240 * | 390 * |
241 * @constructor | 391 * @constructor |
242 * @extends {HTMLDivElement} | 392 * @extends {HTMLDivElement} |
243 */ | 393 */ |
244 var Timeline = cr.ui.define('div'); | 394 var Timeline = cr.ui.define('div'); |
(...skipping 18 matching lines...) Expand all Loading... |
263 | 413 |
264 this.bindEventListener_(document, 'keypress', this.onKeypress_, this); | 414 this.bindEventListener_(document, 'keypress', this.onKeypress_, this); |
265 this.bindEventListener_(document, 'keydown', this.onKeydown_, this); | 415 this.bindEventListener_(document, 'keydown', this.onKeydown_, this); |
266 this.bindEventListener_(document, 'mousedown', this.onMouseDown_, this); | 416 this.bindEventListener_(document, 'mousedown', this.onMouseDown_, this); |
267 this.bindEventListener_(document, 'mousemove', this.onMouseMove_, this); | 417 this.bindEventListener_(document, 'mousemove', this.onMouseMove_, this); |
268 this.bindEventListener_(document, 'mouseup', this.onMouseUp_, this); | 418 this.bindEventListener_(document, 'mouseup', this.onMouseUp_, this); |
269 this.bindEventListener_(document, 'dblclick', this.onDblClick_, this); | 419 this.bindEventListener_(document, 'dblclick', this.onDblClick_, this); |
270 | 420 |
271 this.lastMouseViewPos_ = {x: 0, y: 0}; | 421 this.lastMouseViewPos_ = {x: 0, y: 0}; |
272 | 422 |
273 this.selection_ = []; | 423 this.selection_ = new TimelineSelection(); |
274 }, | 424 }, |
275 | 425 |
276 /** | 426 /** |
277 * Wraps the standard addEventListener but automatically binds the provided | 427 * Wraps the standard addEventListener but automatically binds the provided |
278 * func to the provided target, tracking the resulting closure. When detach | 428 * func to the provided target, tracking the resulting closure. When detach |
279 * is called, these listeners will be automatically removed. | 429 * is called, these listeners will be automatically removed. |
280 */ | 430 */ |
281 bindEventListener_: function(object, event, func, target) { | 431 bindEventListener_: function(object, event, func, target) { |
282 if (!this.boundListeners_) | 432 if (!this.boundListeners_) |
283 this.boundListeners_ = []; | 433 this.boundListeners_ = []; |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
423 // Set up a reasonable viewport. | 573 // Set up a reasonable viewport. |
424 this.viewport_.setWhenPossible(function() { | 574 this.viewport_.setWhenPossible(function() { |
425 var w = this.firstCanvas.width; | 575 var w = this.firstCanvas.width; |
426 this.viewport_.xSetWorldRange(this.model_.minTimestamp, | 576 this.viewport_.xSetWorldRange(this.model_.minTimestamp, |
427 this.model_.maxTimestamp, | 577 this.model_.maxTimestamp, |
428 w); | 578 w); |
429 }.bind(this)); | 579 }.bind(this)); |
430 }, | 580 }, |
431 | 581 |
432 /** | 582 /** |
| 583 * @param {TimelineFilter} filter The filter to use for finding matches. |
| 584 * @param {TimelineSelection} selection The selection to add matches to. |
433 * @return {Array} An array of objects that match the provided | 585 * @return {Array} An array of objects that match the provided |
434 * TimelineFilter. | 586 * TimelineFilter. |
435 */ | 587 */ |
436 findAllObjectsMatchingFilter: function(filter) { | 588 addAllObjectsMatchingFilterToSelection: function(filter, selection) { |
437 var hits = []; | 589 for (var i = 0; i < this.tracks_.children.length; ++i) |
438 for (var i = 0; i < this.tracks_.children.length; ++i) { | 590 this.tracks_.children[i].addAllObjectsMatchingFilterToSelection( |
439 var trackHits = | 591 filter, selection); |
440 this.tracks_.children[i].findAllObjectsMatchingFilter(filter); | |
441 Array.prototype.push.apply(hits, trackHits); | |
442 } | |
443 return hits; | |
444 }, | 592 }, |
445 | 593 |
446 /** | 594 /** |
447 * @return {Element} The element whose focused state determines | 595 * @return {Element} The element whose focused state determines |
448 * whether to respond to keyboard inputs. | 596 * whether to respond to keyboard inputs. |
449 * Defaults to the parent element. | 597 * Defaults to the parent element. |
450 */ | 598 */ |
451 get focusElement() { | 599 get focusElement() { |
452 if (this.focusElement_) | 600 if (this.focusElement_) |
453 return this.focusElement_; | 601 return this.focusElement_; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 case 68: // D | 667 case 68: // D |
520 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); | 668 vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5); |
521 break; | 669 break; |
522 } | 670 } |
523 }, | 671 }, |
524 | 672 |
525 // Not all keys send a keypress. | 673 // Not all keys send a keypress. |
526 onKeydown_: function(e) { | 674 onKeydown_: function(e) { |
527 if (!this.listenToKeys_) | 675 if (!this.listenToKeys_) |
528 return; | 676 return; |
| 677 var sel; |
529 switch (e.keyCode) { | 678 switch (e.keyCode) { |
530 case 37: // left arrow | 679 case 37: // left arrow |
531 this.selectPrevious_(e); | 680 sel = this.selection.getShiftedSelection(-1); |
| 681 if (sel) { |
| 682 this.setSelectionAndMakeVisible(sel); |
| 683 e.preventDefault(); |
| 684 } |
532 break; | 685 break; |
533 case 39: // right arrow | 686 case 39: // right arrow |
534 this.selectNext_(e); | 687 sel = this.selection.getShiftedSelection(1); |
| 688 if (sel) { |
| 689 this.setSelectionAndMakeVisible(sel); |
| 690 e.preventDefault(); |
| 691 } |
535 break; | 692 break; |
536 case 9: // TAB | 693 case 9: // TAB |
537 if (this.focusElement.tabIndex == -1) { | 694 if (this.focusElement.tabIndex == -1) { |
538 if (e.shiftKey) | 695 if (e.shiftKey) |
539 this.selectPrevious_(e); | 696 this.selectPrevious_(e); |
540 else | 697 else |
541 this.selectNext_(e); | 698 this.selectNext_(e); |
542 e.preventDefault(); | 699 e.preventDefault(); |
543 } | 700 } |
544 break; | 701 break; |
545 } | 702 } |
546 }, | 703 }, |
547 | 704 |
548 /** | 705 /** |
549 * Zoom in or out on the timeline by the given scale factor. | 706 * Zoom in or out on the timeline by the given scale factor. |
550 * @param {integer} scale The scale factor to apply. If <1, zooms out. | 707 * @param {integer} scale The scale factor to apply. If <1, zooms out. |
551 */ | 708 */ |
552 zoomBy_: function(scale) { | 709 zoomBy_: function(scale) { |
553 if (!this.firstCanvas) | 710 if (!this.firstCanvas) |
554 return; | 711 return; |
555 var vp = this.viewport_; | 712 var vp = this.viewport_; |
556 var viewWidth = this.firstCanvas.clientWidth; | 713 var viewWidth = this.firstCanvas.clientWidth; |
557 var curMouseV = this.lastMouseViewPos_.x; | 714 var curMouseV = this.lastMouseViewPos_.x; |
558 var curCenterW = vp.xViewToWorld(curMouseV); | 715 var curCenterW = vp.xViewToWorld(curMouseV); |
559 vp.scaleX = vp.scaleX * scale; | 716 vp.scaleX = vp.scaleX * scale; |
560 vp.xPanWorldPosToViewPos(curCenterW, curMouseV, viewWidth); | 717 vp.xPanWorldPosToViewPos(curCenterW, curMouseV, viewWidth); |
561 }, | 718 }, |
562 | 719 |
563 /** Select the next slice on the timeline. Applies to each track. */ | |
564 selectNext_: function(e) { | |
565 if (this.selectAdjoining_(true)) | |
566 e.preventDefault(); | |
567 }, | |
568 | |
569 /** Select the previous slice on the timeline. Applies to each track. */ | |
570 selectPrevious_: function(e) { | |
571 if (this.selectAdjoining_(false)) | |
572 e.preventDefault(); | |
573 }, | |
574 | |
575 /** | |
576 * Helper for selection previous or next. | |
577 * @param {boolean} forwardp If true, select one forward (next). | |
578 * Else, select previous. | |
579 * @return {boolean} true if current selection changed. | |
580 */ | |
581 selectAdjoining_: function(forwardp) { | |
582 var i, track, slice, adjoining; | |
583 var selection = []; | |
584 var minTime = Number.MAX_VALUE; | |
585 var maxTime = -Number.MAX_VALUE; | |
586 // Try and select next. | |
587 for (i = 0; i < this.selection_.length; i++) { | |
588 adjoining = undefined; | |
589 track = this.selection_[i].track; | |
590 slice = this.selection_[i].slice; | |
591 if (slice) { | |
592 if (forwardp) | |
593 adjoining = track.pickNext(slice); | |
594 else | |
595 adjoining = track.pickPrevious(slice); | |
596 } | |
597 if (adjoining != undefined) { | |
598 selection.push({track: track, slice: adjoining}); | |
599 if (slice.start < minTime) | |
600 minTime = slice.start; | |
601 if (slice.start + slice.duration > maxTime) | |
602 maxTime = slice.start + slice.duration; | |
603 } | |
604 } | |
605 if (selection.length == 0) { | |
606 // Nothing adjoining was found; leave the current selection. | |
607 return false; | |
608 } | |
609 this.selection = selection; | |
610 | |
611 // Potentially move the viewport to keep the new selection in view. | |
612 this.viewport_.xPanWorldRangeIntoView(minTime, maxTime, | |
613 this.firstCanvas.width); | |
614 return true; | |
615 }, | |
616 | |
617 get keyHelp() { | 720 get keyHelp() { |
618 var help = 'Keyboard shortcuts:\n' + | 721 var help = 'Keyboard shortcuts:\n' + |
619 ' w/s : Zoom in/out (with shift: go faster)\n' + | 722 ' w/s : Zoom in/out (with shift: go faster)\n' + |
620 ' a/d : Pan left/right\n' + | 723 ' a/d : Pan left/right\n' + |
621 ' e : Center on mouse\n' + | 724 ' e : Center on mouse\n' + |
622 ' g/G : Shows grid at the start/end of the selected task\n'; | 725 ' g/G : Shows grid at the start/end of the selected task\n'; |
623 | 726 |
624 if (this.focusElement.tabIndex) { | 727 if (this.focusElement.tabIndex) { |
625 help += ' <- : Select previous event on current timeline\n' + | 728 help += ' <- : Select previous event on current timeline\n' + |
626 ' -> : Select next event on current timeline\n'; | 729 ' -> : Select next event on current timeline\n'; |
627 } else { | 730 } else { |
628 help += ' <-,^TAB : Select previous event on current timeline\n' + | 731 help += ' <-,^TAB : Select previous event on current timeline\n' + |
629 ' ->, TAB : Select next event on current timeline\n'; | 732 ' ->, TAB : Select next event on current timeline\n'; |
630 } | 733 } |
631 help += | 734 help += |
632 '\n' + | 735 '\n' + |
633 'Dbl-click to zoom in; Shift dbl-click to zoom out\n'; | 736 'Dbl-click to zoom in; Shift dbl-click to zoom out\n'; |
634 return help; | 737 return help; |
635 }, | 738 }, |
636 | 739 |
637 get selection() { | 740 get selection() { |
638 return this.selection_; | 741 return this.selection_; |
639 }, | 742 }, |
640 | 743 |
641 set selection(selection) { | 744 set selection(selection) { |
| 745 if (!(selection instanceof TimelineSelection)) |
| 746 throw 'Expected TimelineSelection'; |
| 747 |
642 // Clear old selection. | 748 // Clear old selection. |
643 var i; | 749 var i; |
644 for (i = 0; i < this.selection_.length; i++) | 750 for (i = 0; i < this.selection_.length; i++) |
645 this.selection_[i].slice.selected = false; | 751 this.selection_[i].selected = false; |
646 | 752 |
647 this.selection_ = selection; | 753 this.selection_ = selection; |
648 | 754 |
649 cr.dispatchSimpleEvent(this, 'selectionChange'); | 755 cr.dispatchSimpleEvent(this, 'selectionChange'); |
650 for (i = 0; i < this.selection_.length; i++) | 756 for (i = 0; i < this.selection_.length; i++) |
651 this.selection_[i].slice.selected = true; | 757 this.selection_[i].selected = true; |
652 this.viewport_.dispatchChangeEvent(); // Triggers a redraw. | 758 this.viewport_.dispatchChangeEvent(); // Triggers a redraw. |
653 }, | 759 }, |
654 | 760 |
655 getSelectionRange: function() { | |
656 var wmin = Infinity; | |
657 var wmax = -wmin; | |
658 for (var i = 0; i < this.selection_.length; i++) { | |
659 var hit = this.selection_[i]; | |
660 if (hit.slice) { | |
661 wmin = Math.min(wmin, hit.slice.start); | |
662 wmax = Math.max(wmax, hit.slice.end); | |
663 } | |
664 } | |
665 return { | |
666 min: wmin, | |
667 max: wmax | |
668 }; | |
669 }, | |
670 | |
671 setSelectionAndMakeVisible: function(selection, zoomAllowed) { | 761 setSelectionAndMakeVisible: function(selection, zoomAllowed) { |
| 762 if (!(selection instanceof TimelineSelection)) |
| 763 throw 'Expected TimelineSelection'; |
672 this.selection = selection; | 764 this.selection = selection; |
673 var range = this.getSelectionRange(); | 765 var range = this.selection.range; |
674 var size = this.viewport_.xWorldVectorToView(range.max - range.min); | 766 var size = this.viewport_.xWorldVectorToView(range.max - range.min); |
675 if (zoomAllowed && size < 50) { | 767 if (zoomAllowed && size < 50) { |
676 var worldCenter = range.min + (range.max - range.min) * 0.5; | 768 var worldCenter = range.min + (range.max - range.min) * 0.5; |
677 var worldRange = (range.max - range.min) * 5; | 769 var worldRange = (range.max - range.min) * 5; |
678 this.viewport_.xSetWorldRange(worldCenter - worldRange * 0.5, | 770 this.viewport_.xSetWorldRange(worldCenter - worldRange * 0.5, |
679 worldCenter + worldRange * 0.5, | 771 worldCenter + worldRange * 0.5, |
680 this.firstCanvas.width); | 772 this.firstCanvas.width); |
681 return; | 773 return; |
682 } | 774 } |
683 | 775 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 | 809 |
718 var e = new cr.Event('selectionChanging'); | 810 var e = new cr.Event('selectionChanging'); |
719 e.loWX = loWX; | 811 e.loWX = loWX; |
720 e.hiWX = hiWX; | 812 e.hiWX = hiWX; |
721 this.dispatchEvent(e); | 813 this.dispatchEvent(e); |
722 }, | 814 }, |
723 | 815 |
724 onGridToggle_: function(left) { | 816 onGridToggle_: function(left) { |
725 var tb; | 817 var tb; |
726 if (left) | 818 if (left) |
727 tb = Math.min.apply(Math, this.selection_.map( | 819 tb = this.selection_.range.min; |
728 function(x) { return x.slice.start; })); | |
729 else | 820 else |
730 tb = Math.max.apply(Math, this.selection_.map( | 821 tb = this.selection_.range.max; |
731 function(x) { return x.slice.end; })); | |
732 | 822 |
733 // Shift the timebase left until its just left of minTimestamp. | 823 // Shift the timebase left until its just left of minTimestamp. |
734 var numInterfvalsSinceStart = Math.ceil((tb - this.model_.minTimestamp) / | 824 var numInterfvalsSinceStart = Math.ceil((tb - this.model_.minTimestamp) / |
735 this.viewport_.gridStep_); | 825 this.viewport_.gridStep_); |
736 this.viewport_.gridTimebase = tb - | 826 this.viewport_.gridTimebase = tb - |
737 (numInterfvalsSinceStart + 1) * this.viewport_.gridStep_; | 827 (numInterfvalsSinceStart + 1) * this.viewport_.gridStep_; |
738 this.viewport_.gridEnabled = true; | 828 this.viewport_.gridEnabled = true; |
739 }, | 829 }, |
740 | 830 |
741 onMouseDown_: function(e) { | 831 onMouseDown_: function(e) { |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
794 var hiX = Math.max(eDown.clientX, e.clientX); | 884 var hiX = Math.max(eDown.clientX, e.clientX); |
795 var loY = Math.min(eDown.clientY, e.clientY); | 885 var loY = Math.min(eDown.clientY, e.clientY); |
796 var hiY = Math.max(eDown.clientY, e.clientY); | 886 var hiY = Math.max(eDown.clientY, e.clientY); |
797 | 887 |
798 // Convert to worldspace. | 888 // Convert to worldspace. |
799 var canv = this.firstCanvas; | 889 var canv = this.firstCanvas; |
800 var loWX = this.viewport_.xViewToWorld(loX - canv.offsetLeft); | 890 var loWX = this.viewport_.xViewToWorld(loX - canv.offsetLeft); |
801 var hiWX = this.viewport_.xViewToWorld(hiX - canv.offsetLeft); | 891 var hiWX = this.viewport_.xViewToWorld(hiX - canv.offsetLeft); |
802 | 892 |
803 // Figure out what has been hit. | 893 // Figure out what has been hit. |
804 (function() { | 894 var selection = new TimelineSelection(); |
805 var selection = []; | 895 for (i = 0; i < this.tracks_.children.length; i++) { |
806 function addHit(type, track, slice) { | 896 var track = this.tracks_.children[i]; |
807 selection.push({track: track, slice: slice}); | 897 |
| 898 // Only check tracks that insersect the rect. |
| 899 var trackClientRect = track.getBoundingClientRect(); |
| 900 var a = Math.max(loY, trackClientRect.top); |
| 901 var b = Math.min(hiY, trackClientRect.bottom); |
| 902 if (a <= b) { |
| 903 track.addIntersectingItemsInRangeToSelection( |
| 904 loWX, hiWX, loY, hiY, selection); |
808 } | 905 } |
809 for (i = 0; i < this.tracks_.children.length; i++) { | 906 } |
810 var track = this.tracks_.children[i]; | 907 // Activate the new selection. |
811 | 908 this.selection = selection; |
812 // Only check tracks that insersect the rect. | |
813 var trackClientRect = track.getBoundingClientRect(); | |
814 var a = Math.max(loY, trackClientRect.top); | |
815 var b = Math.min(hiY, trackClientRect.bottom); | |
816 if (a <= b) { | |
817 track.pickRange(loWX, hiWX, loY, hiY, addHit); | |
818 } | |
819 } | |
820 // Activate the new selection. | |
821 this.selection = selection; | |
822 }).call(this); | |
823 } | 909 } |
824 }, | 910 }, |
825 | 911 |
826 onDblClick_: function(e) { | 912 onDblClick_: function(e) { |
827 var canv = this.firstCanvas; | 913 var canv = this.firstCanvas; |
828 if (e.x < canv.offsetLeft) | 914 if (e.x < canv.offsetLeft) |
829 return; | 915 return; |
830 | 916 |
831 var scale = 4; | 917 var scale = 4; |
832 if (e.shiftKey) | 918 if (e.shiftKey) |
833 scale = 1 / scale; | 919 scale = 1 / scale; |
834 this.zoomBy_(scale); | 920 this.zoomBy_(scale); |
835 e.preventDefault(); | 921 e.preventDefault(); |
836 } | 922 } |
837 }; | 923 }; |
838 | 924 |
839 /** | 925 /** |
840 * The TimelineModel being viewed by the timeline | 926 * The TimelineModel being viewed by the timeline |
841 * @type {TimelineModel} | 927 * @type {TimelineModel} |
842 */ | 928 */ |
843 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); | 929 cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS); |
844 | 930 |
845 return { | 931 return { |
846 Timeline: Timeline, | 932 Timeline: Timeline, |
| 933 TimelineSelectionSliceHit: TimelineSelectionSliceHit, |
| 934 TimelineSelectionCounterSampleHit: TimelineSelectionCounterSampleHit, |
| 935 TimelineSelection: TimelineSelection, |
847 TimelineViewport: TimelineViewport | 936 TimelineViewport: TimelineViewport |
848 }; | 937 }; |
849 }); | 938 }); |
OLD | NEW |