Index: third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js |
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js |
index c6e391c880a3e9c5475ffacebc794c9dc61b5639..d3ae3340b796edbd70bee6e65458f976c44c783a 100644 |
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js |
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js |
@@ -11,21 +11,25 @@ |
// you may not use this file except in compliance with the License. |
// Licensed under the Apache License, Version 2.0 (the "License"); |
// |
+/** |
+ * @fileoverview The canvas that handles gesture typing for inputview. |
+ */ |
goog.provide('i18n.input.chrome.inputview.elements.content.GestureCanvasView'); |
-goog.provide('i18n.input.chrome.inputview.elements.content.GestureCanvasView.GestureStroke'); |
-goog.provide('i18n.input.chrome.inputview.elements.content.GestureCanvasView.Point'); |
-goog.require('goog.async.Delay'); |
goog.require('goog.dom.TagName'); |
goog.require('goog.dom.classlist'); |
goog.require('goog.style'); |
+goog.require('i18n.input.chrome.ElementType'); |
goog.require('i18n.input.chrome.inputview.Css'); |
goog.require('i18n.input.chrome.inputview.elements.Element'); |
-goog.require('i18n.input.chrome.inputview.elements.ElementType'); |
+goog.require('i18n.input.chrome.inputview.elements.content.GestureStroke'); |
+goog.require('i18n.input.chrome.inputview.elements.content.Point'); |
goog.scope(function() { |
var Css = i18n.input.chrome.inputview.Css; |
-var ElementType = i18n.input.chrome.inputview.elements.ElementType; |
+var ElementType = i18n.input.chrome.ElementType; |
+var GestureStroke = i18n.input.chrome.inputview.elements.content.GestureStroke; |
+var Point = i18n.input.chrome.inputview.elements.content.Point; |
var TagName = goog.dom.TagName; |
@@ -33,7 +37,8 @@ var TagName = goog.dom.TagName; |
/** |
* The gesture canvas view. |
* |
- * This view is used to display the strokes for gesture typing. |
+ * This view is used to display the strokes for gesture typing, and handles |
+ * stroke lifetime management and rendering logic. |
* |
* @param {goog.events.EventTarget=} opt_eventTarget The parent event target. |
* @constructor |
@@ -68,332 +73,17 @@ i18n.input.chrome.inputview.elements.content.GestureCanvasView = |
/** |
* A list of list of gesture points to be rendered on the canvas as strokes. |
* |
- * @private {!Array<GestureStroke>} |
+ * @private {!Array<!GestureStroke>} |
*/ |
this.strokeList_ = []; |
- |
- /** @private {!goog.async.Delay} */ |
- this.animator_ = new goog.async.Delay(this.animateGestureTrail_, 0, this); |
}; |
var GestureCanvasView = |
i18n.input.chrome.inputview.elements.content.GestureCanvasView; |
goog.inherits(GestureCanvasView, i18n.input.chrome.inputview.elements.Element); |
- |
-/** |
- * A single stroke on the canvas. |
- * |
- * @constructor |
- */ |
-i18n.input.chrome.inputview.elements.content.GestureCanvasView.GestureStroke = |
- function() { |
- /** |
- * The list of points that make up this stroke. |
- * |
- * @type {!Array.<!Point>} |
- */ |
- this.points = []; |
- |
- /** |
- * Whether or not this stroke is considered active. i.e. whether or not it |
- * should be considered for rendering and decoding. |
- * |
- * @private {boolean} |
- */ |
- this.isActive_ = false; |
- |
- /** |
- * The time the first point was added to this stroke. Used to keep all points |
- * relative to the first point. |
- * |
- * @private {number} |
- */ |
- this.firstTime_ = 0; |
-}; |
-var GestureStroke = |
- i18n.input.chrome.inputview.elements.content.GestureCanvasView |
- .GestureStroke; |
- |
- |
-/** |
- * Rate at which the ttl should degrade for the fading stroke effect. |
- * |
- * @const {number} |
- */ |
-GestureStroke.DEGRADATION_RATE = 5; |
- |
- |
-/** |
- * Starting time-to-live value. |
- * |
- * @const {number} |
- */ |
-GestureStroke.STARTING_TTL = 255; |
- |
- |
-// TODO(stevet): This is temporary and needs to be updated with a dynamic value |
-// that considers other parameters like the width of character keys. |
-/** |
- * Distance threshold for when a stroke is considered active, in pixels. |
- * |
- * @const {number} |
- */ |
-GestureStroke.ACTIVE_THRESHOLD = 40; |
- |
- |
-/** |
- * Starting red value. |
- * |
- * @const {number} |
- */ |
-GestureStroke.STARTING_R_VALUE = 0; |
- |
- |
-/** |
- * Starting green value. |
- * |
- * @const {number} |
- */ |
-GestureStroke.STARTING_G_VALUE = 180; |
- |
- |
-/** |
- * Starting blue value. |
- * |
- * @const {number} |
- */ |
-GestureStroke.STARTING_B_VALUE = 204; |
- |
- |
-/** |
- * Calculates the color of the point based on the ttl. |
- * |
- * @param {number} ttl The time to live of the point. |
- * @return {string} The color to use for the point. |
- * @private |
- */ |
-GestureStroke.calculateColor_ = function(ttl) { |
- // TODO(maxw): Use this percentage to fade the stroke color. |
- var remainingTtlPercentage = ttl / GestureStroke.STARTING_TTL; |
- var rValue = GestureStroke.STARTING_R_VALUE; |
- var gValue = GestureStroke.STARTING_G_VALUE; |
- var bValue = GestureStroke.STARTING_B_VALUE; |
- return 'rgb(' + rValue + ', ' + gValue + ', ' + bValue + ')'; |
-}; |
- |
- |
-/** |
- * Calculates the distance between two points. |
- * |
- * @param {!Point} first The first point. |
- * @param {!Point} second The second point. |
- * @return {number} The number of pixels between first and second. |
- * @private |
- */ |
-GestureStroke.calculateDistance_ = function(first, second) { |
- // Simply use the Pythagorean. |
- var a = Math.abs(first.x - second.x); |
- var b = Math.abs(first.y - second.y); |
- return Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); |
-}; |
- |
- |
-/** |
- * Calculates the line width of the point based on the ttl. |
- * |
- * @param {number} ttl The time to live of the point. |
- * @return {number} The line width to use for the point. |
- * @private |
- */ |
-GestureStroke.calculateLineWidth_ = function(ttl) { |
- var ratio = ttl / GestureStroke.STARTING_TTL; |
- if (ratio < 0) { |
- ratio = 0; |
- } |
- return 9 * ratio; |
-}; |
- |
- |
-/** |
- * Degrades all the points in this stroke. |
- * |
- * @return {boolean} Returns true if it was possible to degrade one or more |
- * points, otherwise it means that this stroke is now empty. |
- */ |
-GestureStroke.prototype.degrade = function() { |
- var all_empty = true; |
- for (var i = 0; i < this.points.length; i++) { |
- if (this.points[i].ttl > 0) { |
- this.points[i].ttl -= GestureStroke.DEGRADATION_RATE; |
- all_empty = false; |
- } |
- } |
- return !all_empty; |
-}; |
- |
- |
-/** |
- * Draw the gesture trail for this stroke onto the canvas context. |
- * |
- * @param {!CanvasRenderingContext2D} context The drawing context to render to. |
- */ |
-GestureStroke.prototype.draw = function(context) { |
- // Only start drawing active strokes. Note that TTL still updates even if a |
- // stroke is not yet active. |
- if (!this.isActive()) { |
- return; |
- } |
- |
- for (var i = 1; i < this.points.length; i++) { |
- var first = this.points[i - 1]; |
- var second = this.points[i]; |
- // All rendering calculations are based on the second point in the segment |
- // because there must be at least two points for something to be rendered. |
- var ttl = second.ttl; |
- if (ttl <= 0) { |
- continue; |
- } |
- |
- context.beginPath(); |
- context.moveTo(first.x, first.y); |
- context.lineTo(second.x, second.y); |
- context.strokeStyle = GestureStroke.calculateColor_(ttl); |
- context.fillStyle = 'none'; |
- context.lineWidth = GestureStroke.calculateLineWidth_(ttl); |
- context.lineCap = 'round'; |
- context.lineJoin = 'round'; |
- context.stroke(); |
- } |
-}; |
- |
- |
/** |
- * Returns true iff this stroke is considered "active". This means that it has |
- * passed a certain threshold and should be considered for rendering and |
- * decoding. |
- * |
- * @return {boolean} Whether or not the stroke is active. |
- */ |
-GestureStroke.prototype.isActive = function() { |
- // Once a stroke is active, it remains active. |
- if (this.isActive_) { |
- return this.isActive_; |
- } |
- |
- if (this.points.length < 2) { |
- return false; |
- } |
- |
- // Calculate the distance between the first point and the latest one. |
- this.isActive_ = GestureStroke.calculateDistance_( |
- this.points[0], this.points[this.points.length - 1]) > |
- GestureStroke.ACTIVE_THRESHOLD; |
- |
- return this.isActive_; |
-}; |
- |
- |
-/** |
- * Add a point to this stroke. |
- * |
- * @param {Point} p The point to add to this stroke. |
- */ |
-GestureStroke.prototype.pushPoint = function(p) { |
- if (this.points.length == 0) { |
- this.firstTime_ = p.time; |
- } |
- // Convert the timestamp so it is relative to the first point, including |
- // setting the first point to zero. |
- p.time -= this.firstTime_; |
- this.points.push(p); |
-}; |
- |
- |
- |
-/** |
- * One point in the gesture stroke. |
- * |
- * This class is used for both rendering the gesture stroke, and also for |
- * transmitting the stroke coordinates to the recognizer for decoding. |
- * |
- * @param {number} x The x coordinate. |
- * @param {number} y The y coordinate. |
- * @param {number} identifier The pointer event identifier. |
- * @constructor |
- */ |
-i18n.input.chrome.inputview.elements.content.GestureCanvasView.Point = |
- function(x, y, identifier) { |
- /** |
- * The left offset relative to the canvas. |
- * |
- * @type {number} |
- */ |
- this.x = x; |
- |
- /** |
- * The top offset relative to the canvas. |
- * |
- * @type {number} |
- */ |
- this.y = y; |
- |
- /** |
- * The pointer ID. |
- * |
- * @type {number} |
- */ |
- this.pointer = 0; |
- |
- /** |
- * The time-to-live value of the point, used to render the trail fading |
- * effect. |
- * |
- * @type {number} |
- */ |
- this.ttl = GestureStroke.STARTING_TTL; |
- |
- /** |
- * The time this point was created, in ms since epoch. |
- * |
- * @type {number} |
- */ |
- this.time = Date.now(); |
- |
- /** |
- * The action type of the point. |
- * |
- * @type {i18n.input.chrome.inputview.elements.content.GestureCanvasView. |
- * Point.Action} |
- */ |
- this.action = i18n.input.chrome.inputview.elements.content.GestureCanvasView. |
- Point.Action.ACTION_MOVE; |
- |
- /** |
- * The pointer event identifier associated with this point. |
- * |
- * @type {number} |
- */ |
- this.identifier = identifier; |
-}; |
-var Point = |
- i18n.input.chrome.inputview.elements.content.GestureCanvasView.Point; |
- |
- |
-/** |
- * Enum describing the type of action for a given point. |
- * |
- * @enum {number} |
- */ |
-Point.Action = { |
- ACTION_DOWN: 0, |
- ACTION_UP: 1, |
- ACTION_MOVE: 2 |
-}; |
- |
- |
-/** |
- * Draw the gesture trail. |
+ * Draws the gesture trail. |
* |
* @private |
*/ |
@@ -410,7 +100,8 @@ GestureCanvasView.prototype.draw_ = function() { |
/** @override */ |
GestureCanvasView.prototype.createDom = function() { |
- goog.base(this, 'createDom'); |
+ GestureCanvasView.base(this, 'createDom'); |
+ |
var dom = this.getDomHelper(); |
var elem = this.getElement(); |
goog.dom.classlist.add(elem, Css.GESTURE_CANVAS_VIEW); |
@@ -420,7 +111,7 @@ GestureCanvasView.prototype.createDom = function() { |
this.drawingContext_ = this.drawingCanvas_.getContext('2d'); |
dom.appendChild(elem, this.drawingCanvas_); |
- this.animator_.start(); |
+ window.requestAnimationFrame(this.animateGestureTrail_.bind(this)); |
}; |
@@ -446,14 +137,13 @@ GestureCanvasView.prototype.addPoint = function(e) { |
// if the user is gesturing. Only check the last stroke and not all the |
// strokes because all previous strokes might be rendering/degrading, but that |
// does not determine if the user is currently gesturing. |
- var was_active = this.latestStrokeActive_(); |
+ var wasActive = this.latestStrokeActive_(); |
if (this.strokeList_.length == 0) { |
this.strokeList_.push(new GestureStroke()); |
} |
var lastStroke = this.strokeList_[this.strokeList_.length - 1]; |
- if (lastStroke.points.length > 0 && |
- e.identifier != lastStroke.points[0].identifier) { |
+ if (lastStroke.length() > 0 && !this.isActiveIdentifier(e.identifier)) { |
// Should only add new points with the same identifier. This ignores pointer |
// events created by, say, a second finger interacting with the screen while |
// an existing gesture is going on. |
@@ -462,14 +152,14 @@ GestureCanvasView.prototype.addPoint = function(e) { |
lastStroke.pushPoint(this.createGesturePoint_(e)); |
// If the new point |e| activated the last stroke, set gesturing to true. |
- if (!was_active && this.latestStrokeActive_()) { |
+ if (!wasActive && this.latestStrokeActive_()) { |
this.isGesturing = true; |
} |
}; |
/** |
- * Clear the view. |
+ * Clears the view. |
*/ |
GestureCanvasView.prototype.clear = function() { |
this.strokeList_ = []; |
@@ -478,8 +168,6 @@ GestureCanvasView.prototype.clear = function() { |
/** |
- * Returns true iff the last stroke is currently active. |
- * |
* @return {boolean} Whether or not the last stroke is active. |
* @private |
*/ |
@@ -495,24 +183,23 @@ GestureCanvasView.prototype.latestStrokeActive_ = function() { |
* Begins a new gesture. |
* |
* @param {!i18n.input.chrome.inputview.events.PointerEvent} e Drag event to |
- * draw. |
+ * draw. |
*/ |
GestureCanvasView.prototype.startStroke = function(e) { |
// If there is currently a stroke and it does not match the identifier of this |
// new point, then ignore this call. This is to prevent a second finger from |
// interrupting an existing stroke. |
- if (this.strokeList_.length > 0 && |
- e.identifier != this.strokeList_[this.strokeList_.length - 1].points[0] |
- .identifier) { |
+ if (this.strokeList_.length > 0 && !this.isActiveIdentifier(e.identifier)) { |
return; |
} |
// Always start a new array to separate previous strokes from this new one. |
this.strokeList_.push(new GestureStroke()); |
var point = this.createGesturePoint_(e); |
- point.action = Point.Action.ACTION_DOWN; |
- // TODO: This line is a NOP since createGesturePoint_ already assigns the |
- // pointer value, but it must be called to prevent closure from optimizing out |
- // the pointer member. This needs to be fixed to use the true pointer ID of e. |
+ point.action = Point.Action.DOWN; |
+ // TODO(stevet): This line is a NOP since createGesturePoint_ already assigns |
+ // the pointer value, but it must be called to prevent closure from optimizing |
+ // out the pointer member. This needs to be fixed to use the true pointer ID |
+ // of e. |
point.pointer = 0; |
this.strokeList_[this.strokeList_.length - 1].pushPoint(point); |
}; |
@@ -525,18 +212,18 @@ GestureCanvasView.prototype.startStroke = function(e) { |
* event to handle. |
*/ |
GestureCanvasView.prototype.endStroke = function(e) { |
- // TODO: Ensure that this gets called even when the final touch event |
- // is not on the client. |
+ // TODO(stevet): Ensure that this gets called even when the final touch event |
+ // is not on the client. |
// Ignore points that do not have the same identifier. |
if (e.identifier != |
- this.strokeList_[this.strokeList_.length - 1].points[0].identifier) { |
+ this.strokeList_[this.strokeList_.length - 1].getIdentifierAt(0)) { |
return; |
} |
// Send the final event. |
var point = this.createGesturePoint_(e); |
- point.action = Point.Action.ACTION_UP; |
+ point.action = Point.Action.UP; |
this.strokeList_[this.strokeList_.length - 1].pushPoint(point); |
this.isGesturing = false; |
}; |
@@ -556,18 +243,41 @@ GestureCanvasView.prototype.getLastStroke = function() { |
/** |
- * The gesture trail animation function. |
+ * @param {number} identifier The identifier to check. |
+ * @return {boolean} Whether or not identifier is the same as the identifier of |
+ * the current active stroke. |
+ */ |
+GestureCanvasView.prototype.isActiveIdentifier = function(identifier) { |
+ return identifier == this.strokeList_[this.strokeList_.length - 1] |
+ .getIdentifierAt(0); |
+}; |
+ |
+ |
+/** |
+ * Removes only empty strokes from the stroke list. |
+ */ |
+GestureCanvasView.prototype.removeEmptyStrokes = function() { |
+ for (var i = 0; i < this.strokeList_.length; i++) { |
+ if (this.strokeList_[i].isDegraded()) { |
+ this.strokeList_.splice(i, 1); |
+ i--; |
+ } |
+ } |
+}; |
+ |
+ |
+/** |
+ * Animates the gesture trail. |
* |
* @private |
*/ |
GestureCanvasView.prototype.animateGestureTrail_ = function() { |
- // TODO(stevet): This approximates drawing at 60fps. Refactor this and the |
- // voice input code to use a common call to requestRenderFrame. |
- var timeStep = 16; |
- |
+ // TODO(stevet): Currently these two methods assume callback at 60fps. They |
+ // should technically be modified to re-draw and update based on the actual |
+ // time passed. |
this.draw_(); |
this.degradeStrokes_(); |
- this.animator_.start(timeStep); |
+ window.requestAnimationFrame(this.animateGestureTrail_.bind(this)); |
}; |
@@ -575,9 +285,9 @@ GestureCanvasView.prototype.animateGestureTrail_ = function() { |
* Returns a gesture point for a given event, with the correct coordinates. |
* |
* @param {!i18n.input.chrome.inputview.events.DragEvent| |
- * i18n.input.chrome.inputview.events.PointerEvent} e The event to |
- * convert. |
- * @return {Point} The converted gesture point. |
+ * i18n.input.chrome.inputview.events.PointerEvent} e The event to |
+ * convert. |
+ * @return {!Point} The converted gesture point. |
* @private |
*/ |
GestureCanvasView.prototype.createGesturePoint_ = function(e) { |
@@ -593,13 +303,7 @@ GestureCanvasView.prototype.createGesturePoint_ = function(e) { |
*/ |
GestureCanvasView.prototype.degradeStrokes_ = function() { |
for (var i = 0; i < this.strokeList_.length; i++) { |
- // In the case where all points in the list are empty, dispose of the first. |
- if (!this.strokeList_[i].degrade()) { |
- this.strokeList_.splice(i, 1); |
- i--; |
- } |
+ this.strokeList_[i].degrade(); |
} |
}; |
- |
- |
}); // goog.scope |