1<!DOCTYPE html>
2<!--
3Copyright (c) 2014 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="import" href="/tracing/base/range.html">
9
10<script>
11'use strict';
12
13tr.exportTo('tr.ui', function() {
14  /**
15   * @constructor
16   */
17  function SnapIndicator(y, height) {
18    this.y = y;
19    this.height = height;
20  }
21
22  /**
23   * The interesting part of the world.
24   *
25   * @constructor
26   */
27  function TimelineInterestRange(vp) {
28    this.viewport_ = vp;
29
30    this.range_ = new tr.b.Range();
31
32    this.leftSelected_ = false;
33    this.rightSelected_ = false;
34
35    this.leftSnapIndicator_ = undefined;
36    this.rightSnapIndicator_ = undefined;
37  }
38
39  TimelineInterestRange.prototype = {
40    get isEmpty() {
41      return this.range_.isEmpty;
42    },
43
44    reset: function() {
45      this.range_.reset();
46      this.leftSelected_ = false;
47      this.rightSelected_ = false;
48      this.leftSnapIndicator_ = undefined;
49      this.rightSnapIndicator_ = undefined;
50      this.viewport_.dispatchChangeEvent();
51    },
52
53    get min() {
54      return this.range_.min;
55    },
56
57    set min(min) {
58      this.range_.min = min;
59      this.viewport_.dispatchChangeEvent();
60    },
61
62    get max() {
63      return this.range_.max;
64    },
65
66    set max(max) {
67      this.range_.max = max;
68      this.viewport_.dispatchChangeEvent();
69    },
70
71    set: function(range) {
72      this.range_.reset();
73      this.range_.addRange(range);
74      this.viewport_.dispatchChangeEvent();
75    },
76
77    setMinAndMax: function(min, max) {
78      this.range_.min = min;
79      this.range_.max = max;
80      this.viewport_.dispatchChangeEvent();
81    },
82
83    get range() {
84      return this.range_.range;
85    },
86
87    asRangeObject: function() {
88      var range = new tr.b.Range();
89      range.addRange(this.range_);
90      return range;
91    },
92
93    get leftSelected() {
94      return this.leftSelected_;
95    },
96
97    set leftSelected(leftSelected) {
98      if (this.leftSelected_ == leftSelected)
99        return;
100      this.leftSelected_ = leftSelected;
101      this.viewport_.dispatchChangeEvent();
102    },
103
104    get rightSelected() {
105      return this.rightSelected_;
106    },
107
108    set rightSelected(rightSelected) {
109      if (this.rightSelected_ == rightSelected)
110        return;
111      this.rightSelected_ = rightSelected;
112      this.viewport_.dispatchChangeEvent();
113    },
114
115    get leftSnapIndicator() {
116      return this.leftSnapIndicator_;
117    },
118
119    set leftSnapIndicator(leftSnapIndicator) {
120      this.leftSnapIndicator_ = leftSnapIndicator;
121      this.viewport_.dispatchChangeEvent();
122    },
123
124    get rightSnapIndicator() {
125      return this.rightSnapIndicator_;
126    },
127
128    set rightSnapIndicator(rightSnapIndicator) {
129      this.rightSnapIndicator_ = rightSnapIndicator;
130      this.viewport_.dispatchChangeEvent();
131    },
132
133    draw: function(ctx, viewLWorld, viewRWorld) {
134      if (this.range_.isEmpty)
135        return;
136      var dt = this.viewport_.currentDisplayTransform;
137
138      var markerLWorld = this.min;
139      var markerRWorld = this.max;
140
141      var markerLView = Math.round(dt.xWorldToView(markerLWorld));
142      var markerRView = Math.round(dt.xWorldToView(markerRWorld));
143
144      ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
145      if (markerLWorld > viewLWorld) {
146        ctx.fillRect(dt.xWorldToView(viewLWorld), 0,
147            markerLView, ctx.canvas.height);
148      }
149
150      if (markerRWorld < viewRWorld) {
151        ctx.fillRect(markerRView, 0,
152            dt.xWorldToView(viewRWorld), ctx.canvas.height);
153      }
154
155      var pixelRatio = window.devicePixelRatio || 1;
156      ctx.lineWidth = Math.round(pixelRatio);
157      if (this.range_.range > 0) {
158        this.drawLine_(ctx, viewLWorld, viewRWorld,
159                       ctx.canvas.height, this.min, this.leftSelected_);
160        this.drawLine_(ctx, viewLWorld, viewRWorld,
161                       ctx.canvas.height, this.max, this.rightSelected_);
162      } else {
163        this.drawLine_(ctx, viewLWorld, viewRWorld,
164                       ctx.canvas.height, this.min,
165                       this.leftSelected_ || this.rightSelected_);
166      }
167      ctx.lineWidth = 1;
168    },
169
170    drawLine_: function(ctx, viewLWorld, viewRWorld, height, ts, selected) {
171      if (ts < viewLWorld || ts >= viewRWorld)
172        return;
173
174      var dt = this.viewport_.currentDisplayTransform;
175      var viewX = Math.round(dt.xWorldToView(ts));
176
177      // Apply subpixel translate to get crisp lines.
178      // http://www.mobtowers.com/html5-canvas-crisp-lines-every-time/
179      ctx.save();
180      ctx.translate((Math.round(ctx.lineWidth) % 2) / 2, 0);
181
182      ctx.beginPath();
183      tr.ui.b.drawLine(ctx, viewX, 0, viewX, height);
184      if (selected)
185        ctx.strokeStyle = 'rgb(255, 0, 0)';
186      else
187        ctx.strokeStyle = 'rgb(0, 0, 0)';
188      ctx.stroke();
189
190      ctx.restore();
191    },
192
193    drawIndicators: function(ctx, viewLWorld, viewRWorld) {
194      if (this.leftSnapIndicator_) {
195        this.drawIndicator_(ctx, viewLWorld, viewRWorld,
196                            this.range_.min,
197                            this.leftSnapIndicator_,
198                            this.leftSelected_);
199      }
200      if (this.rightSnapIndicator_) {
201        this.drawIndicator_(ctx, viewLWorld, viewRWorld,
202                            this.range_.max,
203                            this.rightSnapIndicator_,
204                            this.rightSelected_);
205      }
206    },
207
208    drawIndicator_: function(ctx, viewLWorld, viewRWorld,
209                             xWorld, si, selected) {
210      var dt = this.viewport_.currentDisplayTransform;
211
212      var viewX = Math.round(dt.xWorldToView(xWorld));
213
214      // Apply subpixel translate to get crisp lines.
215      // http://www.mobtowers.com/html5-canvas-crisp-lines-every-time/
216      ctx.save();
217      ctx.translate((Math.round(ctx.lineWidth) % 2) / 2, 0);
218
219      var pixelRatio = window.devicePixelRatio || 1;
220      var viewY = si.y * devicePixelRatio;
221      var viewHeight = si.height * devicePixelRatio;
222      var arrowSize = 4 * pixelRatio;
223
224      if (selected)
225        ctx.fillStyle = 'rgb(255, 0, 0)';
226      else
227        ctx.fillStyle = 'rgb(0, 0, 0)';
228      tr.ui.b.drawTriangle(ctx,
229          viewX - arrowSize * 0.75, viewY,
230          viewX + arrowSize * 0.75, viewY,
231          viewX, viewY + arrowSize);
232      ctx.fill();
233      tr.ui.b.drawTriangle(ctx,
234          viewX - arrowSize * 0.75, viewY + viewHeight,
235          viewX + arrowSize * 0.75, viewY + viewHeight,
236          viewX, viewY + viewHeight - arrowSize);
237      ctx.fill();
238
239      ctx.restore();
240    }
241  };
242
243  return {
244    SnapIndicator: SnapIndicator,
245    TimelineInterestRange: TimelineInterestRange
246  };
247});
248</script>
249