1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="stylesheet" href="/tracing/ui/tracks/drawing_container.css">
9
10<link rel="import" href="/tracing/base/raf.html">
11<link rel="import" href="/tracing/ui/tracks/track.html">
12<link rel="import" href="/tracing/ui/base/constants.html">
13<link rel="import" href="/tracing/ui/base/ui.html">
14
15<script>
16'use strict';
17
18tr.exportTo('tr.ui.tracks', function() {
19  var DrawType = {
20    GENERAL_EVENT: 1,
21    INSTANT_EVENT: 2,
22    BACKGROUND: 3,
23    GRID: 4,
24    FLOW_ARROWS: 5,
25    MARKERS: 6,
26    HIGHLIGHTS: 7,
27    ANNOTATIONS: 8
28  };
29
30  var DrawingContainer = tr.ui.b.define('drawing-container',
31                                        tr.ui.tracks.Track);
32
33  DrawingContainer.prototype = {
34    __proto__: tr.ui.tracks.Track.prototype,
35
36    decorate: function(viewport) {
37      tr.ui.tracks.Track.prototype.decorate.call(this, viewport);
38      this.classList.add('drawing-container');
39
40      this.canvas_ = document.createElement('canvas');
41      this.canvas_.className = 'drawing-container-canvas';
42      this.canvas_.style.left = tr.ui.b.constants.HEADING_WIDTH + 'px';
43      this.appendChild(this.canvas_);
44
45      this.ctx_ = this.canvas_.getContext('2d');
46
47      this.viewportChange_ = this.viewportChange_.bind(this);
48      this.viewport.addEventListener('change', this.viewportChange_);
49    },
50
51    // Needed to support the calls in TimelineTrackView.
52    get canvas() {
53      return this.canvas_;
54    },
55
56    context: function() {
57      return this.ctx_;
58    },
59
60    viewportChange_: function() {
61      this.invalidate();
62    },
63
64    invalidate: function() {
65      if (this.rafPending_)
66        return;
67      this.rafPending_ = true;
68
69      tr.b.requestPreAnimationFrame(this.preDraw_, this);
70    },
71
72    preDraw_: function() {
73      this.rafPending_ = false;
74      this.updateCanvasSizeIfNeeded_();
75
76      tr.b.requestAnimationFrameInThisFrameIfPossible(this.draw_, this);
77    },
78
79    draw_: function() {
80      this.ctx_.clearRect(0, 0, this.canvas_.width, this.canvas_.height);
81
82      var typesToDraw = [
83        DrawType.BACKGROUND,
84        DrawType.HIGHLIGHTS,
85        DrawType.GRID,
86        DrawType.INSTANT_EVENT,
87        DrawType.GENERAL_EVENT,
88        DrawType.MARKERS,
89        DrawType.ANNOTATIONS,
90        DrawType.FLOW_ARROWS
91      ];
92
93      for (var idx in typesToDraw) {
94        for (var i = 0; i < this.children.length; ++i) {
95          if (!(this.children[i] instanceof tr.ui.tracks.Track))
96            continue;
97          this.children[i].drawTrack(typesToDraw[idx]);
98        }
99      }
100
101      var pixelRatio = window.devicePixelRatio || 1;
102      var bounds = this.canvas_.getBoundingClientRect();
103      var dt = this.viewport.currentDisplayTransform;
104      var viewLWorld = dt.xViewToWorld(0);
105      var viewRWorld = dt.xViewToWorld(
106          bounds.width * pixelRatio);
107
108      this.viewport.drawGridLines(this.ctx_, viewLWorld, viewRWorld);
109    },
110
111    updateCanvasSizeIfNeeded_: function() {
112      var visibleChildTracks =
113          tr.b.asArray(this.children).filter(this.visibleFilter_);
114
115      var thisBounds = this.getBoundingClientRect();
116
117      var firstChildTrackBounds = visibleChildTracks[0].getBoundingClientRect();
118      var lastChildTrackBounds =
119          visibleChildTracks[visibleChildTracks.length - 1].
120              getBoundingClientRect();
121
122      var innerWidth = firstChildTrackBounds.width -
123          tr.ui.b.constants.HEADING_WIDTH;
124      var innerHeight = lastChildTrackBounds.bottom - firstChildTrackBounds.top;
125
126      var pixelRatio = window.devicePixelRatio || 1;
127      if (this.canvas_.width != innerWidth * pixelRatio) {
128        this.canvas_.width = innerWidth * pixelRatio;
129        this.canvas_.style.width = innerWidth + 'px';
130      }
131
132      if (this.canvas_.height != innerHeight * pixelRatio) {
133        this.canvas_.height = innerHeight * pixelRatio;
134        this.canvas_.style.height = innerHeight + 'px';
135      }
136    },
137
138    visibleFilter_: function(element) {
139      if (!(element instanceof tr.ui.tracks.Track))
140        return false;
141      return window.getComputedStyle(element).display !== 'none';
142    },
143
144    addClosestEventToSelection: function(
145        worldX, worldMaxDist, loY, hiY, selection) {
146      for (var i = 0; i < this.children.length; ++i) {
147        if (!(this.children[i] instanceof tr.ui.tracks.Track))
148          continue;
149        var trackClientRect = this.children[i].getBoundingClientRect();
150        var a = Math.max(loY, trackClientRect.top);
151        var b = Math.min(hiY, trackClientRect.bottom);
152        if (a <= b) {
153          this.children[i].addClosestEventToSelection(
154              worldX, worldMaxDist, loY, hiY, selection);
155        }
156      }
157
158      tr.ui.tracks.Track.prototype.addClosestEventToSelection.
159          apply(this, arguments);
160    },
161
162    addEventsToTrackMap: function(eventToTrackMap) {
163      for (var i = 0; i < this.children.length; ++i) {
164        if (!(this.children[i] instanceof tr.ui.tracks.Track))
165          continue;
166        this.children[i].addEventsToTrackMap(eventToTrackMap);
167      }
168    }
169  };
170
171  return {
172    DrawingContainer: DrawingContainer,
173    DrawType: DrawType
174  };
175});
176</script>
177