3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
8 <link rel="import" href="/tracing/constants.html">
9 <link rel="import" href="/tracing/selection.html">
10 <link rel="import" href="/tvcm/range.html">
11 <link rel="import" href="/tvcm/ui.html">
12 <link rel="import" href="/tracing/trace_model/slice.html">
18 * @fileoverview Provides the TimingTool class.
20 tvcm.exportTo('tracing', function() {
22 var constants = tracing.constants;
25 * Tool for taking time measurements in the TimelineTrackView using
29 function TimingTool(viewport, targetElement) {
30 this.viewport_ = viewport;
32 // Prepare the event handlers to be added and removed repeatedly.
33 this.onMouseMove_ = this.onMouseMove_.bind(this);
34 this.onDblClick_ = this.onDblClick_.bind(this);
35 this.targetElement_ = targetElement;
37 // Valid only during mousedown.
38 this.isMovingLeftEdge_ = false;
41 TimingTool.prototype = {
43 onEnterTiming: function(e) {
44 this.targetElement_.addEventListener('mousemove', this.onMouseMove_);
45 this.targetElement_.addEventListener('dblclick', this.onDblClick_);
48 onBeginTiming: function(e) {
49 var pt = this.getSnappedToEventPosition_(e);
50 this.mouseDownAt_(pt.x, pt.y);
52 this.updateSnapIndicators_(pt);
55 updateSnapIndicators_: function(pt) {
58 var ir = this.viewport_.interestRange;
60 ir.leftSnapIndicator = new tracing.SnapIndicator(pt.y, pt.height);
62 ir.rightSnapIndicator = new tracing.SnapIndicator(pt.y, pt.height);
65 onUpdateTiming: function(e) {
66 var pt = this.getSnappedToEventPosition_(e);
67 this.mouseMoveAt_(pt.x, pt.y, true);
68 this.updateSnapIndicators_(pt);
71 onEndTiming: function(e) {
75 onExitTiming: function(e) {
76 this.targetElement_.removeEventListener('mousemove', this.onMouseMove_);
77 this.targetElement_.removeEventListener('dblclick', this.onDblClick_);
80 onMouseMove_: function(e) {
83 var worldX = this.getWorldXFromEvent_(e);
84 this.mouseMoveAt_(worldX, e.clientY, false);
87 onDblClick_: function(e) {
88 // TODO(nduca): Implement dobuleclicking.
89 console.error('not implemented');
92 ////////////////////////////////////////////////////////////////////////////
94 mouseDownAt_: function(worldX, y) {
95 var ir = this.viewport_.interestRange;
96 var dt = this.viewport_.currentDisplayTransform;
98 var pixelRatio = window.devicePixelRatio || 1;
99 var nearnessThresholdWorld = dt.xViewVectorToWorld(6 * pixelRatio);
102 ir.setMinAndMax(worldX, worldX);
103 ir.rightSelected = true;
104 this.isMovingLeftEdge_ = false;
110 if (Math.abs(worldX - ir.min) < nearnessThresholdWorld) {
111 ir.leftSelected = true;
113 this.isMovingLeftEdge_ = true;
118 if (Math.abs(worldX - ir.max) < nearnessThresholdWorld) {
119 ir.rightSelected = true;
121 this.isMovingLeftEdge_ = false;
125 ir.setMinAndMax(worldX, worldX);
126 ir.rightSelected = true;
127 this.isMovingLeftEdge_ = false;
130 mouseMoveAt_: function(worldX, y, mouseDown) {
131 var ir = this.viewport_.interestRange;
134 this.updateMovingEdge_(worldX);
138 var ir = this.viewport_.interestRange;
139 var dt = this.viewport_.currentDisplayTransform;
141 var pixelRatio = window.devicePixelRatio || 1;
142 var nearnessThresholdWorld = dt.xViewVectorToWorld(6 * pixelRatio);
145 if (Math.abs(worldX - ir.min) < nearnessThresholdWorld) {
146 ir.leftSelected = true;
147 ir.rightSelected = false;
152 if (Math.abs(worldX - ir.max) < nearnessThresholdWorld) {
153 ir.leftSelected = false;
154 ir.rightSelected = true;
158 ir.leftSelected = false;
159 ir.rightSelected = false;
163 updateMovingEdge_: function(newWorldX) {
164 var ir = this.viewport_.interestRange;
167 if (this.isMovingLeftEdge_)
173 ir.setMinAndMax(a, b);
175 ir.setMinAndMax(b, a);
177 if (ir.min == newWorldX) {
178 this.isMovingLeftEdge_ = true;
179 ir.leftSelected = true;
180 ir.rightSelected = false;
182 this.isMovingLeftEdge_ = false;
183 ir.leftSelected = false;
184 ir.rightSelected = true;
188 mouseUp_: function() {
189 var dt = this.viewport_.currentDisplayTransform;
190 var ir = this.viewport_.interestRange;
192 ir.leftSelected = false;
193 ir.rightSelected = false;
195 var pixelRatio = window.devicePixelRatio || 1;
196 var minWidthValue = dt.xViewVectorToWorld(2 * pixelRatio);
197 if (ir.range < minWidthValue)
201 getWorldXFromEvent_: function(e) {
202 var pixelRatio = window.devicePixelRatio || 1;
203 var canvas = this.viewport_.modelTrackContainer.canvas;
204 var worldOffset = canvas.getBoundingClientRect().left;
205 var viewX = (e.clientX - worldOffset) * pixelRatio;
206 return this.viewport_.currentDisplayTransform.xViewToWorld(viewX);
211 * Get the closest position of an event within a vertical range of the mouse
212 * position if possible, otherwise use the position of the mouse pointer.
213 * @param {MouseEvent} e Mouse event with the current mouse coordinates.
215 * {Number} x, The x coordinate in world space.
216 * {Number} y, The y coordinate in world space.
217 * {Number} height, The height of the event.
218 * {boolean} snapped Whether the coordinates are from a snapped event or
219 * the mouse position.
222 getSnappedToEventPosition_: function(e) {
223 var pixelRatio = window.devicePixelRatio || 1;
224 var EVENT_SNAP_RANGE = 16 * pixelRatio;
226 var modelTrackContainer = this.viewport_.modelTrackContainer;
227 var modelTrackContainerRect = modelTrackContainer.getBoundingClientRect();
229 var viewport = this.viewport_;
230 var dt = viewport.currentDisplayTransform;
231 var worldMaxDist = dt.xViewVectorToWorld(EVENT_SNAP_RANGE);
233 var worldX = this.getWorldXFromEvent_(e);
234 var mouseY = e.clientY;
236 var selection = new tracing.Selection();
238 // Look at the track under mouse position first for better performance.
239 modelTrackContainer.addClosestEventToSelection(
240 worldX, worldMaxDist, mouseY, mouseY, selection);
242 // Look at all tracks visible on screen.
243 if (!selection.length) {
244 modelTrackContainer.addClosestEventToSelection(
245 worldX, worldMaxDist,
246 modelTrackContainerRect.top, modelTrackContainerRect.bottom,
250 var minDistX = worldMaxDist;
251 var minDistY = Infinity;
252 var pixWidth = dt.xViewVectorToWorld(1);
254 // Create result object with the mouse coordinates.
257 y: mouseY - modelTrackContainerRect.top,
262 var eventBounds = new tvcm.Range();
263 for (var i = 0; i < selection.length; i++) {
264 var event = selection[i];
265 var track = viewport.trackForEvent(event);
266 var trackRect = track.getBoundingClientRect();
269 event.addBoundsToRange(eventBounds);
271 if (Math.abs(eventBounds.min - worldX) <
272 Math.abs(eventBounds.max - worldX)) {
273 eventX = eventBounds.min;
275 eventX = eventBounds.max;
278 var distX = eventX - worldX;
280 var eventY = trackRect.top;
281 var eventHeight = trackRect.height;
282 var distY = Math.abs(eventY + eventHeight / 2 - mouseY);
284 // Prefer events with a closer y position if their x difference is below
285 // the width of a pixel.
286 if ((distX <= minDistX || Math.abs(distX - minDistX) < pixWidth) &&
291 // Retrieve the event position from the hit.
294 modelTrackContainer.scrollTop - modelTrackContainerRect.top;
295 result.height = eventHeight;
296 result.snapped = true;
305 TimingTool: TimingTool