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/rect_track.css">
9
10<link rel="import" href="/tracing/base/sorted_array_utils.html">
11<link rel="import" href="/tracing/model/proxy_selectable_item.html">
12<link rel="import" href="/tracing/ui/base/draw_helpers.html">
13<link rel="import" href="/tracing/ui/base/fast_rect_renderer.html">
14<link rel="import" href="/tracing/ui/base/heading.html">
15<link rel="import" href="/tracing/ui/base/ui.html">
16<link rel="import" href="/tracing/ui/tracks/track.html">
17
18<script>
19'use strict';
20
21tr.exportTo('tr.ui.tracks', function() {
22
23  /**
24   * A track that displays an array of Rect objects.
25   * @constructor
26   * @extends {Track}
27   */
28  var RectTrack = tr.ui.b.define(
29      'rect-track', tr.ui.tracks.Track);
30
31  RectTrack.prototype = {
32
33    __proto__: tr.ui.tracks.Track.prototype,
34
35    decorate: function(viewport) {
36      tr.ui.tracks.Track.prototype.decorate.call(this, viewport);
37      this.classList.add('rect-track');
38      this.asyncStyle_ = false;
39      this.rects_ = null;
40
41      this.heading_ = document.createElement('tr-ui-heading');
42      this.appendChild(this.heading_);
43    },
44
45    set heading(heading) {
46      this.heading_.heading = heading;
47    },
48
49    get heading() {
50      return this.heading_.heading;
51    },
52
53    set tooltip(tooltip) {
54      this.heading_.tooltip = tooltip;
55    },
56
57    set selectionGenerator(generator) {
58      this.heading_.selectionGenerator = generator;
59    },
60
61    set expanded(expanded) {
62      this.heading_.expanded = !!expanded;
63    },
64
65    set arrowVisible(arrowVisible) {
66      this.heading_.arrowVisible = !!arrowVisible;
67    },
68
69    get expanded() {
70      return this.heading_.expanded;
71    },
72
73    get asyncStyle() {
74      return this.asyncStyle_;
75    },
76
77    set asyncStyle(v) {
78      this.asyncStyle_ = !!v;
79    },
80
81    get rects() {
82      return this.rects_;
83    },
84
85    set rects(rects) {
86      this.rects_ = rects || [];
87      this.invalidateDrawingContainer();
88    },
89
90    get height() {
91      return window.getComputedStyle(this).height;
92    },
93
94    set height(height) {
95      this.style.height = height;
96      this.invalidateDrawingContainer();
97    },
98
99    get hasVisibleContent() {
100      return this.rects_.length > 0;
101    },
102
103    draw: function(type, viewLWorld, viewRWorld) {
104      switch (type) {
105        case tr.ui.tracks.DrawType.GENERAL_EVENT:
106          this.drawRects_(viewLWorld, viewRWorld);
107          break;
108      }
109    },
110
111    drawRects_: function(viewLWorld, viewRWorld) {
112      var ctx = this.context();
113
114      ctx.save();
115      var bounds = this.getBoundingClientRect();
116      tr.ui.b.drawSlices(
117          ctx,
118          this.viewport.currentDisplayTransform,
119          viewLWorld,
120          viewRWorld,
121          bounds.height,
122          this.rects_,
123          this.asyncStyle_);
124      ctx.restore();
125
126      if (bounds.height <= 6)
127        return;
128
129      var fontSize, yOffset;
130      if (bounds.height < 15) {
131        fontSize = 6;
132        yOffset = 1.0;
133      } else {
134        fontSize = 10;
135        yOffset = 2.5;
136      }
137      tr.ui.b.drawLabels(
138          ctx,
139          this.viewport.currentDisplayTransform,
140          viewLWorld,
141          viewRWorld,
142          this.rects_,
143          this.asyncStyle_,
144          fontSize,
145          yOffset);
146    },
147
148    addEventsToTrackMap: function(eventToTrackMap) {
149      if (this.rects_ === undefined || this.rects_ === null)
150        return;
151
152      this.rects_.forEach(function(rect) {
153        rect.addToTrackMap(eventToTrackMap, this);
154      }, this);
155    },
156
157    addIntersectingEventsInRangeToSelectionInWorldSpace: function(
158        loWX, hiWX, viewPixWidthWorld, selection) {
159      function onRect(rect) {
160        rect.addToSelection(selection);
161      }
162      onRect = onRect.bind(this);
163      var instantEventWidth = 2 * viewPixWidthWorld;
164      tr.b.iterateOverIntersectingIntervals(this.rects_,
165          function(x) { return x.start; },
166          function(x) { return x.duration == 0 ?
167                               x.duration + instantEventWidth :
168                               x.duration; },
169          loWX, hiWX,
170          onRect);
171    },
172
173    /**
174     * Add the item to the left or right of the provided event, if any, to the
175     * selection.
176     * @param {rect} The current rect.
177     * @param {Number} offset Number of rects away from the event to look.
178     * @param {Selection} selection The selection to add an event to,
179     * if found.
180     * @return {boolean} Whether an event was found.
181     * @private
182     */
183    addEventNearToProvidedEventToSelection: function(event, offset, selection) {
184      var index = tr.b.findFirstIndexInArray(this.rects_, function(rect) {
185        return rect.modelItem === event;
186      });
187      if (index === -1)
188        return false;
189
190      var newIndex = index + offset;
191      if (newIndex < 0 || newIndex >= this.rects_.length)
192        return false;
193
194      this.rects_[newIndex].addToSelection(selection);
195      return true;
196    },
197
198    addAllEventsMatchingFilterToSelection: function(filter, selection) {
199      for (var i = 0; i < this.rects_.length; ++i) {
200        // TODO(petrcermak): Rather than unpacking the proxy item here,
201        // we should probably add an addToSelectionIfMatching(selection, filter)
202        // method to SelectableItem (#900).
203        var modelItem = this.rects_[i].modelItem;
204        if (!modelItem)
205          continue;
206        if (filter.matchSlice(modelItem))
207          selection.push(modelItem);
208      }
209    },
210
211    addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY,
212                                         selection) {
213      var rect = tr.b.findClosestIntervalInSortedIntervals(
214          this.rects_,
215          function(x) { return x.start; },
216          function(x) { return x.end; },
217          worldX,
218          worldMaxDist);
219
220      if (!rect)
221        return;
222
223      rect.addToSelection(selection);
224    }
225  };
226
227  /**
228   * A filled rectangle with a title.
229   *
230   * @constructor
231   * @extends {ProxySelectableItem}
232   */
233  function Rect(modelItem, title, colorId, start, duration) {
234    tr.model.ProxySelectableItem.call(this, modelItem);
235    this.title = title;
236    this.colorId = colorId;
237    this.start = start;
238    this.duration = duration;
239    this.end = start + duration;
240  };
241
242  Rect.prototype = {
243    __proto__: tr.model.ProxySelectableItem.prototype
244  };
245
246  return {
247    RectTrack: RectTrack,
248    Rect: Rect
249  };
250});
251</script>
252