Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(474)

Unified Diff: third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js

Issue 1257313003: Update Google Input Tools (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Free up grd resources. Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js
index e341c6e925f100f04359393d2b94c0700c7e58f0..43836e4eec452450fa8973a7c1856a9825b8366a 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js
@@ -15,7 +15,13 @@ goog.provide('i18n.input.chrome.inputview.elements.content.SwipeView');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classlist');
+goog.require('goog.events');
+goog.require('goog.fx.Animation');
+goog.require('goog.fx.dom.FadeOut');
+goog.require('goog.fx.dom.PredefinedEffect');
+goog.require('goog.fx.easing');
goog.require('goog.style');
+goog.require('i18n.input.chrome.Statistics');
goog.require('i18n.input.chrome.inputview.Css');
goog.require('i18n.input.chrome.inputview.SwipeDirection');
goog.require('i18n.input.chrome.inputview.elements.Element');
@@ -27,11 +33,14 @@ goog.require('i18n.input.chrome.inputview.util');
goog.require('i18n.input.chrome.message.ContextType');
goog.scope(function() {
+var CandidateView = i18n.input.chrome.inputview.elements.content.CandidateView;
+var Container = i18n.input.chrome.inputview.KeyboardContainer;
var ContextType = i18n.input.chrome.message.ContextType;
var Css = i18n.input.chrome.inputview.Css;
var ElementType = i18n.input.chrome.inputview.elements.ElementType;
var EventType = i18n.input.chrome.inputview.events.EventType;
var KeyCodes = i18n.input.chrome.inputview.events.KeyCodes;
+var SwipeDirection = i18n.input.chrome.inputview.SwipeDirection;
var content = i18n.input.chrome.inputview.elements.content;
var util = i18n.input.chrome.inputview.util;
@@ -41,12 +50,13 @@ var util = i18n.input.chrome.inputview.util;
* The view used to display the selection and deletion swipe tracks.
*
* @param {!i18n.input.chrome.inputview.Adapter} adapter .
+ * @param {!CandidateView} candidateView .
* @param {goog.events.EventTarget=} opt_eventTarget The parent event target.
* @constructor
* @extends {i18n.input.chrome.inputview.elements.Element}
*/
i18n.input.chrome.inputview.elements.content.SwipeView = function(
- adapter, opt_eventTarget) {
+ adapter, candidateView, opt_eventTarget) {
i18n.input.chrome.inputview.elements.content.SwipeView.base(
this, 'constructor', '', ElementType.SWIPE_VIEW, opt_eventTarget);
@@ -58,6 +68,13 @@ i18n.input.chrome.inputview.elements.content.SwipeView = function(
this.adapter_ = adapter;
/**
+ * The candidate view.
+ *
+ * @private {!CandidateView}
+ */
+ this.candidateView_ = candidateView;
+
+ /**
* The swipe elements.
*
* @private {!Array<!Element>}
@@ -113,6 +130,20 @@ i18n.input.chrome.inputview.elements.content.SwipeView = function(
this.coverElement_;
/**
+ * The finger tracker affordance.
+ *
+ * @private {!Element}
+ */
+ this.fingerTracker_;
+
+ /**
+ * The ripple element.
+ *
+ * @private {!Element}
+ */
+ this.ripple_;
+
+ /**
* The index of the alternative element which is highlighted.
*
* @private {number}
@@ -122,7 +153,7 @@ i18n.input.chrome.inputview.elements.content.SwipeView = function(
/**
* The key which triggered this view to be shown.
*
- * @type {!i18n.input.chrome.inputview.elements.content.SoftKey}
+ * @type {i18n.input.chrome.inputview.elements.content.SoftKey|undefined}
*/
this.triggeredBy;
@@ -140,6 +171,75 @@ i18n.input.chrome.inputview.elements.content.SwipeView = function(
*/
this.armed_ = false;
+ /**
+ * Whether to handle swipe events.
+ *
+ * @type {boolean}
+ */
+ this.enabled = false;
+
+ /**
+ * Whether the current keyset supports swipe editing.
+ *
+ * @private {boolean}
+ */
+ this.isKeysetSupported_ = false;
+
+ /**
+ * Fade animation for the finger affordance.
+ *
+ * @private {!goog.fx.dom.PredefinedEffect}
+ */
+ this.fadeAnimation_;
+
+ /**
+ * Scale animation for the finger affordance.
+ *
+ * @private {!goog.fx.dom.PredefinedEffect}
+ */
+ this.scaleAnimation_;
+
+ /**
+ * The statistics object for recording metrics values.
+ *
+ * @type {!i18n.input.chrome.Statistics}
+ * @private
+ */
+ this.statistics_ = i18n.input.chrome.Statistics.getInstance();
+
+ /**
+ * Total number of forward movements per swipe. This is relative to the track
+ * and so swiping left on a left-to-right track, or swiping right on a
+ * left-to-right track.
+ *
+ * @private {number}
+ */
+ this.forwardMoves_ = 0;
+
+ /**
+ * Total number of backward movements per swipe. This is relative to the track
+ * and so swiping right on a left-to-right track, or swiping left on a
+ * left-to-right track.
+ *
+ * @private {number}
+ */
+ this.backwardMoves_ = 0;
+
+
+ /**
+ * Whether the current track is selection or deletion.
+ *
+ * @private {boolean}
+ */
+ this.isSelection_ = true;
+
+
+ /**
+ * Triggering event identifier.
+ *
+ * @private {number|undefined}
+ */
+ this.eventIdentifier_;
};
goog.inherits(i18n.input.chrome.inputview.elements.content.SwipeView,
i18n.input.chrome.inputview.elements.Element);
@@ -152,7 +252,7 @@ var SwipeView = i18n.input.chrome.inputview.elements.content.SwipeView;
* @private {number}
* @const
*/
-SwipeView.LENGTH_ = 7;
+SwipeView.LENGTH_ = 15;
/**
@@ -171,7 +271,7 @@ SwipeView.INVALID_INDEX_ = -1;
* @private {number}
* @const
*/
-SwipeView.FINGER_DISTANCE_TO_CANCEL_SWIPE_ = 20;
+SwipeView.FINGER_DISTANCE_TO_CANCEL_SWIPE_ = 100;
/**
@@ -180,34 +280,83 @@ SwipeView.FINGER_DISTANCE_TO_CANCEL_SWIPE_ = 20;
* @private {number}
* @const
*/
-SwipeView.SEGMENT_WIDTH_ = 70;
+SwipeView.SEGMENT_WIDTH_ = 40;
+
+
+/**
+ * The maximum surrounding text length that's provided.
+ *
+ * @private {number}
+ * @const
+ */
+SwipeView.MAX_SURROUNDING_TEXT_LENGTH_ = 100;
+
+
+/**
+ * The string representation of &nbsp.
+ *
+ * @private {string}
+ * @const
+ */
+SwipeView.NBSP_CHAR_ = String.fromCharCode(160);
/**
- * The width of a large track segment.
+ * The width of the finger affordance.
*
* @private {number}
* @const
*/
-SwipeView.LARGE_SEGMENT_WIDTH_ = 100;
+SwipeView.FINGER_TRACKER_WIDTH_ = 64;
/**
- * The maximum surrounding text length that's provided.
+ * The initial width of the ripple.
*
* @private {number}
* @const
*/
-SwipeView.MAX_SURROUNDING_TEXT_LENGTH_ = 100;
+SwipeView.RIPPLE_WIDTH_ = 22;
/**
- * The string representation of &nbsp.
+ * The ripple scale animation duration in ms.
*
- * @private {string}
+ * @private {number}
* @const
*/
-SwipeView.NBSP_CHAR_ = String.fromCharCode(160);
+SwipeView.SCALE_ANIMATION_TIME_ = 220;
+
+
+/**
+ * The ripple scale factor.
+ *
+ * @private {number}
+ * @const
+ */
+SwipeView.RIPPLE_SCALE_FACTOR_ = 24;
+
+
+/**
+ * The ripple fade animation duration in ms.
+ *
+ * @private {number}
+ * @const
+ */
+SwipeView.FADE_ANIMATION_TIME_ = 200;
+
+
+/**
+ * Tooltips to display in the candidate window during gesture editing.
+ *
+ * @enum {string}
+ */
+SwipeView.Tooltip = {
+ SELECTION: chrome.i18n.getMessage('SWIPE_SELECTION_TOOLTIP'),
+ DELETION: chrome.i18n.getMessage('SWIPE_DELETION_TOOLTIP'),
+ RESTORATION: chrome.i18n.getMessage('SWIPE_RESTORATION_TOOLTIP')
+};
+var Tooltip = SwipeView.Tooltip;
/**
@@ -234,13 +383,15 @@ SwipeView.prototype.onSurroundingTextChanged_ = function(e) {
this.surroundingTextFocus_ = 0;
return;
}
-
this.surroundingTextAnchor_ = e.anchor;
this.surroundingTextFocus_ = e.focus;
-
var text = e.text || '';
var oldText = this.surroundingText_;
var diff = '';
+ if (oldText == text) {
+ console.error('Duplicate surrounding text event.');
+ return;
+ }
if (util.isLetterDelete(oldText, text)) {
diff = oldText.slice(-1);
// Check if the transformation from oldtext to text was a single letter being
@@ -293,10 +444,10 @@ SwipeView.prototype.onSurroundingTextChanged_ = function(e) {
SwipeView.prototype.swipeToDelete_ = function(e) {
// Cache whether we were tracking.
var alreadyTracking = this.tracking_;
- var changed = this.highlightItem(e.x, e.y);
- var direction = e.direction;
+ var previousIndex = this.getHighlightedIndex();
+ var direction = this.swipeOnTrack(e.x, e.y);
// Did not move segments.
- if (!changed) {
+ if (!direction) {
// First gesture.
if (!alreadyTracking) {
// All previous deletions count as one now.
@@ -304,40 +455,65 @@ SwipeView.prototype.swipeToDelete_ = function(e) {
var word = this.deletedWords_.join('');
this.deletedWords_ = [word];
// Swiped right, cancel the deletion.
- if (direction & i18n.input.chrome.inputview.SwipeDirection.RIGHT) {
+ if (e.direction & SwipeDirection.RIGHT) {
word = this.deletedWords_.pop();
if (word) {
this.adapter_.commitText(word);
}
+ } else {
+ // Change the tooltip to show restoration instructions.
+ this.candidateView_.showTooltip(Tooltip.RESTORATION);
}
}
return;
}
-
- if (direction & i18n.input.chrome.inputview.SwipeDirection.LEFT) {
- this.adapter_.sendKeyDownAndUpEvent(
- '\u0008', KeyCodes.BACKSPACE, undefined, undefined, {
- ctrl: true,
- shift: false
- });
- } else if (direction & i18n.input.chrome.inputview.SwipeDirection.RIGHT) {
- var word = this.deletedWords_.pop();
- if (word) {
- this.adapter_.commitText(word);
+ // Always show restoration tooltip after the trackIndex changes.
+ this.candidateView_.showTooltip(Tooltip.RESTORATION);
+ // Some finger swipes jump tracks, compensate for this.
+ var delta = Math.abs(this.getHighlightedIndex() - previousIndex);
+ if (direction & SwipeDirection.LEFT) {
+ this.forwardMoves_ += delta;
+ for (var i = 0; i < delta; i++) {
+ this.adapter_.sendKeyDownAndUpEvent(
+ '\u0008', KeyCodes.BACKSPACE, undefined, undefined, {
+ ctrl: true,
+ shift: false
+ });
}
- // Restore text we deleted before the track came up, but part of the
- // same gesture.
- if (this.isAtOrigin()) {
- word = this.deletedWords_.pop();
+ } else if (direction & SwipeDirection.RIGHT) {
+ this.backwardMoves_ += delta;
+ for (var i = 0; i < delta; i++) {
+ var word = this.deletedWords_.pop();
if (word) {
this.adapter_.commitText(word);
}
+ // Restore text we deleted before the track came up, but part of the
+ // same gesture.
+ if (this.isAtOrigin()) {
+ word = this.deletedWords_.pop();
+ if (word) {
+ this.adapter_.commitText(word);
+ }
+ break;
+ }
}
+ } else {
+ console.error('Unexpected swipe direction: ' + direction);
}
};
/**
+ * Sets whether the current keyset supports swipe editting.
+ *
+ * @param {boolean} supported .
+ */
+SwipeView.prototype.setKeysetSupported = function(supported) {
+ this.isKeysetSupported_ = supported;
+};
+
+
+/**
* Handles swipe actions on the selection track. Swipes cause the cursor to move
* to the next blank space in the direction of the swipe.
*
@@ -347,9 +523,10 @@ SwipeView.prototype.swipeToDelete_ = function(e) {
SwipeView.prototype.swipeToSelect_ = function(e) {
// Cache whether we were tracking as highlight may change this.
var alreadyTracking = this.tracking_;
- var changed = this.highlightItem(e.x, e.y);
- // First finger movement is onto the blank track. Ignore.
- if (!alreadyTracking || !changed) {
+ var previousIndex = this.getHighlightedIndex();
+ var direction = this.swipeOnTrack(e.x, e.y);
+ // Swipe did not change track index, ignore.
+ if (!direction) {
return;
}
var index = this.getHighlightedIndex();
@@ -359,20 +536,35 @@ SwipeView.prototype.swipeToSelect_ = function(e) {
}
// TODO: Set selectWord to true if the shift key is currently pressed.
var selectWord = false;
- var direction = e.direction;
var code;
- if (direction & i18n.input.chrome.inputview.SwipeDirection.LEFT) {
+ if (direction & SwipeDirection.LEFT) {
code = KeyCodes.ARROW_LEFT;
- } else if (direction & i18n.input.chrome.inputview.SwipeDirection.RIGHT) {
+ } else if (direction & SwipeDirection.RIGHT) {
code = KeyCodes.ARROW_RIGHT;
} else {
+ console.error('Unexpected swipe direction: ' + direction);
return;
}
- this.adapter_.sendKeyDownAndUpEvent(
- '', code, undefined, undefined, {
- ctrl: true,
- shift: selectWord
- });
+ // Finger swipes sometimes go over multiple tracks. Complete the action for
+ // each.
+ var delta = Math.abs(index - previousIndex);
+ if (delta < 0) {
+ console.error('Swipe index did not change.');
+ }
+ if (this.ltr && code == KeyCodes.ARROW_LEFT ||
+ !this.ltr && code == KeyCodes.ARROW_RIGHT) {
+ this.forwardMoves_ += delta;
+ } else {
+ this.backwardMoves_ += delta;
+ }
+ // TODO: Investigate why pointerbundle skips some swipe events.
+ for (var i = 0; i < delta; i++) {
+ this.adapter_.sendKeyDownAndUpEvent(
+ '', code, undefined, undefined, {
+ ctrl: true,
+ shift: selectWord
+ });
+ }
};
@@ -384,6 +576,10 @@ SwipeView.prototype.swipeToSelect_ = function(e) {
* @private
*/
SwipeView.prototype.handleSwipeAction_ = function(e) {
+ if (this.eventIdentifier_ != undefined &&
+ this.eventIdentifier_ != e.identifier) {
+ return;
+ }
if (this.isVisible()) {
if (e.view.type == ElementType.BACKSPACE_KEY) {
this.swipeToDelete_(e);
@@ -405,9 +601,9 @@ SwipeView.prototype.handleSwipeAction_ = function(e) {
if (e.direction & i18n.input.chrome.inputview.SwipeDirection.LEFT) {
var key = /** @type {!content.FunctionalKey} */ (e.view);
// Equivalent to a longpress.
- if (this.adapter_.isGestureDeletionEnabled()) {
- this.showDeletionTrack(key);
- }
+ if (this.isDeletionEnabled()) {
+ this.showDeletionTrack(key, e.identifier, true);
+ }
}
return;
}
@@ -424,6 +620,10 @@ SwipeView.prototype.handlePointerAction_ = function(e) {
if (!e.view) {
return;
}
+ if (this.eventIdentifier_ != undefined &&
+ e.identifier != this.eventIdentifier_) {
+ return;
+ }
switch (e.view.type) {
case ElementType.BACKSPACE_KEY:
var key = /** @type {!content.FunctionalKey} */ (e.view);
@@ -438,28 +638,28 @@ SwipeView.prototype.handlePointerAction_ = function(e) {
this.armed_ = false;
}
} else if (e.type == EventType.LONG_PRESS) {
- if (this.adapter_.isGestureDeletionEnabled()) {
- this.showDeletionTrack(key);
- }
+ if (this.isDeletionEnabled()) {
+ this.showDeletionTrack(key, e.identifier, false);
+ }
}
break;
case ElementType.SWIPE_VIEW:
if (e.type == EventType.POINTER_DOWN &&
e.target == this.getCoverElement()) {
- this.hide();
+ this.recordAndHide_();
} else if (e.type == EventType.POINTER_UP ||
e.type == EventType.POINTER_OUT) {
- this.hide();
+ this.recordAndHide_();
// Reset the deleted words.
this.deletedWords_ = [];
}
break;
case ElementType.SELECT_VIEW:
if (e.type == EventType.POINTER_DOWN) {
- this.showSelectionTrack(e.x, e.y);
+ this.showSelectionTrack(e.x, e.y, e.identifier);
}
if (e.type == EventType.POINTER_UP) {
- this.hide();
+ this.recordAndHide_();
}
break;
}
@@ -472,13 +672,31 @@ SwipeView.prototype.createDom = function() {
var dom = this.getDomHelper();
var elem = this.getElement();
+ goog.style.setElementShown(elem, false);
goog.dom.classlist.add(elem, i18n.input.chrome.inputview.Css.SWIPE_VIEW);
this.coverElement_ = dom.createDom(goog.dom.TagName.DIV,
i18n.input.chrome.inputview.Css.TRACK_COVER);
dom.appendChild(document.body, this.coverElement_);
goog.style.setElementShown(this.coverElement_, false);
-
this.coverElement_['view'] = this;
+ // Cache finger affordance.
+ this.fingerTracker_ = dom.createDom(goog.dom.TagName.DIV,
+ i18n.input.chrome.inputview.Css.GESTURE_EDITING_FINGER_TRACKER);
+ goog.style.setSize(this.fingerTracker_,
+ SwipeView.FINGER_TRACKER_WIDTH_ + 'px',
+ SwipeView.FINGER_TRACKER_WIDTH_ + 'px');
+ this.ripple_ = dom.createDom(goog.dom.TagName.DIV,
+ i18n.input.chrome.inputview.Css.GESTURE_RIPPLE);
+ dom.appendChild(this.coverElement_, this.ripple_);
+ // Cache ripple animations.
+ this.fadeAnimation_ = new goog.fx.dom.FadeOut(this.ripple_,
+ SwipeView.FADE_ANIMATION_TIME_, goog.fx.easing.easeIn);
+ this.scaleAnimation_ = new ScaleAtPoint(this.ripple_,
+ [1, 1],
+ [SwipeView.RIPPLE_SCALE_FACTOR_, SwipeView.RIPPLE_SCALE_FACTOR_],
+ [-SwipeView.RIPPLE_WIDTH_, -SwipeView.RIPPLE_WIDTH_],
+ SwipeView.SCALE_ANIMATION_TIME_,
+ goog.fx.easing.easeOut);
};
@@ -497,7 +715,6 @@ SwipeView.prototype.enterDocument = function() {
EventType.POINTER_UP,
EventType.POINTER_DOWN,
EventType.POINTER_OUT], this.handlePointerAction_);
- goog.style.setElementShown(this.getElement(), false);
};
@@ -508,20 +725,15 @@ SwipeView.prototype.enterDocument = function() {
* @param {number} y
* @param {number} width The width of a key.
* @param {number} height The height of a key.
- * @param {number} firstTrackWidth The width of the first key.
- * @param {number} firstSegmentWidth The width of the first buffer segment.
- * @param {string=} opt_character Characters on each key.
- * @param {Css=} opt_css Optional icon css class.
* @private
*/
-SwipeView.prototype.showDeletionTrack_ = function(x, y, width, height,
- firstTrackWidth, firstSegmentWidth, opt_character, opt_css) {
+SwipeView.prototype.showDeletionTrack_ = function(x, y, width, height) {
this.tracking_ = false;
goog.style.setElementShown(this.getElement(), true);
this.getDomHelper().removeChildren(this.getElement());
+ goog.dom.classlist.add(this.getElement(), Css.DELETION_TRACK);
// Each key except last has a separator.
- var totalWidth = ((2 * SwipeView.LENGTH_) - 3) * width;
- totalWidth += firstTrackWidth + firstSegmentWidth;
+ var totalWidth = ((2 * SwipeView.LENGTH_) - 1) * width;
this.ltr = true;
this.highlightIndex_ = 0;
@@ -531,30 +743,37 @@ SwipeView.prototype.showDeletionTrack_ = function(x, y, width, height,
this.ltr = false;
this.highlightIndex_ = SwipeView.LENGTH_ - 1;
}
- if (firstTrackWidth == 0) {
+ if (width == 0) {
this.highlightIndex_ = SwipeView.INVALID_INDEX_;
}
+ if (this.ltr) {
+ goog.dom.classlist.add(this.getElement(), Css.LEFT_TO_RIGHT);
+ }
var ltr = this.ltr;
- var isFirstSegment = function(i) {
- return ltr ? i == 0 : i == SwipeView.LENGTH_ - 2;
- };
- var isFirstTrackPiece = function(i) {
- return ltr ? i == 0 : i == SwipeView.LENGTH_ - 1;
- };
for (var i = 0; i < SwipeView.LENGTH_; i++) {
- var trackWidth = isFirstTrackPiece(i) ? firstTrackWidth : width;
- if (trackWidth != 0) {
- var keyElem = this.addKey_(opt_character, opt_css);
- goog.style.setSize(keyElem, trackWidth, height);
- this.trackElements_.push(keyElem);
- }
+ var keyElem = this.addKey_();
+ goog.style.setSize(keyElem, width, height);
+ this.trackElements_.push(keyElem);
if (i != (SwipeView.LENGTH_ - 1)) {
- var segmentWidth = isFirstSegment(i) ? firstSegmentWidth : width;
- this.addSeparator_(segmentWidth, height);
+ this.addSeparator_(width, height);
}
}
- goog.style.setPosition(this.getElement(), x, y);
+ // Set position only changes the left and top values, which is problematic.
+ // Manually modify css rules instead.
+ if (this.ltr) {
+ goog.style.setStyle(this.getElement(), {
+ 'right': 'initial',
+ 'left': x,
+ 'top': y
+ });
+ } else {
+ goog.style.setStyle(this.getElement(), {
+ 'left': 'initial',
+ 'right': screen.width - x - totalWidth,
+ 'top': y
+ });
+ }
// Highlight selected element if it's index is valid.
if (this.highlightIndex_ != SwipeView.INVALID_INDEX_) {
var elem = this.trackElements_[this.highlightIndex_];
@@ -566,20 +785,36 @@ SwipeView.prototype.showDeletionTrack_ = function(x, y, width, height,
/**
+ * Shows the current finger location at the coordinates provided.
+ *
+ * @param {number} x .
+ * @param {number} y .
+ * @private
+ */
+SwipeView.prototype.showFinger_ = function(x, y) {
+ var dom = this.getDomHelper();
+ dom.appendChild(this.getElement(), this.fingerTracker_);
+ goog.style.setPosition(this.fingerTracker_,
+ x - (SwipeView.FINGER_TRACKER_WIDTH_ / 2),
+ y - (SwipeView.FINGER_TRACKER_WIDTH_ / 2));
+ goog.style.setElementShown(this.fingerTracker_, true);
+};
+
+
+/**
* Shows the selection swipe tracker.
*
* @param {number} x
* @param {number} y
* @param {number} width The width of a key.
* @param {number} height The height of a key.
- * @param {string} character Characters on each key.
* @private
*/
-SwipeView.prototype.showSelectionTrack_ = function(x, y, width, height,
- character) {
+SwipeView.prototype.showSelectionTrack_ = function(x, y, width, height) {
this.tracking_ = false;
goog.style.setElementShown(this.getElement(), true);
this.getDomHelper().removeChildren(this.getElement());
+ goog.dom.classlist.add(this.getElement(), Css.SELECTION_TRACK);
// Each key has a separator.
var totalWidth = ((2 * SwipeView.LENGTH_)) * width;
@@ -590,11 +825,14 @@ SwipeView.prototype.showSelectionTrack_ = function(x, y, width, height,
x -= totalWidth;
this.ltr = false;
}
+ if (this.ltr) {
+ goog.dom.classlist.add(this.getElement(), Css.LEFT_TO_RIGHT);
+ }
for (var i = 0; i < SwipeView.LENGTH_; i++) {
var keyElem;
if (!this.ltr) {
- keyElem = this.addKey_(character);
+ keyElem = this.addKey_();
goog.style.setSize(keyElem, width, height);
this.trackElements_.push(keyElem);
}
@@ -604,12 +842,26 @@ SwipeView.prototype.showSelectionTrack_ = function(x, y, width, height,
this.trackElements_.push(keyElem);
if (this.ltr) {
- keyElem = this.addKey_(character);
+ keyElem = this.addKey_();
goog.style.setSize(keyElem, width, height);
this.trackElements_.push(keyElem);
}
}
- goog.style.setPosition(this.getElement(), x, y);
+ // Set position only changes the left and top values, which is problematic.
+ // Manually modify css rules instead.
+ if (this.ltr) {
+ goog.style.setStyle(this.getElement(), {
+ 'right': 'initial',
+ 'left': x,
+ 'top': y
+ });
+ } else {
+ goog.style.setStyle(this.getElement(), {
+ 'left': 'initial',
+ 'right': 0,
+ 'top': y
+ });
+ }
goog.style.setElementShown(this.coverElement_, true);
this.triggeredBy && this.triggeredBy.setHighlighted(true);
};
@@ -620,8 +872,14 @@ SwipeView.prototype.showSelectionTrack_ = function(x, y, width, height,
*
* @param {!i18n.input.chrome.inputview.elements.content.SoftKey} key
* The key triggered this track view.
+ * @param {!number} id The triggering event id.
+ * @param {boolean} isSwipe Indicates the trigger was a swipe.
*/
-SwipeView.prototype.showDeletionTrack = function(key) {
+SwipeView.prototype.showDeletionTrack = function(key, id, isSwipe) {
+ this.eventIdentifier_ = id;
+ this.isSelection_ = false;
+ this.adapter_.setGestureEditingInProgress(true, isSwipe);
+ this.candidateView_.showTooltip(Tooltip.DELETION);
this.triggeredBy = key;
var coordinate = goog.style.getClientPosition(key.getElement());
if (key.type == ElementType.BACKSPACE_KEY) {
@@ -629,12 +887,12 @@ SwipeView.prototype.showDeletionTrack = function(key) {
coordinate.x + key.availableWidth,
coordinate.y,
SwipeView.SEGMENT_WIDTH_,
- key.availableHeight,
- key.availableWidth,
- SwipeView.LARGE_SEGMENT_WIDTH_,
- undefined,
- Css.BACKSPACE_ICON);
+ SwipeView.SEGMENT_WIDTH_);
}
+ var centerX = coordinate.x + (key.availableWidth / 2);
+ var centerY = coordinate.y + (key.availableHeight / 2);
+ this.animateRipple_(centerX, centerY);
+ this.showFinger_(centerX, centerY);
};
@@ -643,8 +901,13 @@ SwipeView.prototype.showDeletionTrack = function(key) {
*
* @param {number} x
* @param {number} y
+ * @param {number} id The triggering event id.
*/
-SwipeView.prototype.showSelectionTrack = function(x, y) {
+SwipeView.prototype.showSelectionTrack = function(x, y, id) {
+ this.eventIdentifier_ = id;
+ this.isSelection_ = true;
+ this.adapter_.setGestureEditingInProgress(true);
+ this.candidateView_.showTooltip(Tooltip.SELECTION);
var ltr = (x <= (screen.width / 2));
var halfWidth = SwipeView.SEGMENT_WIDTH_ / 2;
// Center track on finger but force containment.
@@ -654,24 +917,146 @@ SwipeView.prototype.showSelectionTrack = function(x, y) {
ltr ? 0 : screen.width,
trackY,
SwipeView.SEGMENT_WIDTH_,
- SwipeView.SEGMENT_WIDTH_,
- x > (screen.width / 2) ? '<' : '>');
+ SwipeView.SEGMENT_WIDTH_);
+ this.showFinger_(x, y);
+};
+
+
+/**
+ * Creates an animation object that will scale an element at a point.
+ *
+ * Start, end and origin should be 2 dimensional arrays
+ *
+ * @param {Element} element Dom Node to be used in the animation.
+ * @param {Array<number>} start 2D array for start x-scale and y-scale.
+ * @param {Array<number>} end 2D array for end x-scale and y-scale.
+ * @param {Array<number>} origin 2D array for origin relative to the elements
+ * current position.
+ * @param {number} time Length of animation in milliseconds.
+ * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
+ * @extends {goog.fx.dom.PredefinedEffect}
+ * @constructor
+ */
+// TODO: Migrate this to the closure animation library.
+var ScaleAtPoint = function(element, start, end, origin, time, opt_acc) {
+ if (start.length != 2 || end.length != 2 || origin.length != 2) {
+ throw Error('Start, end and origin arrays must be 2D');
+ }
+
+ /**
+ * Point at which the animation should scale relative to the elements current
+ * location.
+ *
+ * @private {Array<number>}
+ */
+ this.origin_ = origin;
+
+ ScaleAtPoint.base(this, 'constructor', element, start, end, time, opt_acc);
+};
+goog.inherits(ScaleAtPoint, goog.fx.dom.PredefinedEffect);
+
+
+/** override */
+ScaleAtPoint.prototype.updateStyle = function() {
+ var transform = [
+ 'translate(', this.origin_[0], 'px, ', this.origin_[1], 'px) ',
+ 'scale(', this.coords[0], ', ', this.coords[1], ')'
+ ].join('');
+ goog.style.setStyle(this.element, 'transform', transform);
+};
+
+
+/**
+ * Handles the fade start event on the ripple.
+ *
+ * @private
+ */
+SwipeView.prototype.onFadeStarted_ = function() {
+ goog.events.unlisten(this.fadeAnimation_,
+ goog.fx.Animation.EventType.BEGIN,
+ this.onFadeStarted_);
+ this.scaleAnimation_.play();
+};
+
+
+/**
+ * Animates the ripple effect centered on the coordinates provided.
+ *
+ * @param {number} x
+ * @param {number} y
+ * @private
+ */
+SwipeView.prototype.animateRipple_ = function(x, y) {
+ goog.style.setPosition(this.ripple_, x, y);
+ goog.style.setStyle(this.ripple_, 'transform', '');
+ goog.style.setElementShown(this.ripple_, true);
+ goog.events.listen(this.fadeAnimation_, goog.fx.Animation.EventType.BEGIN,
+ this.onFadeStarted_.bind(this));
+ this.fadeAnimation_.play();
+};
+
+
+/**
+ * Records statistics for this swipe and hides the track.
+ *
+ * @private
+ */
+SwipeView.prototype.recordAndHide_ = function() {
+ this.recordStatistics_();
+ this.hide_();
};
/**
* Hides the swipe view.
+ *
+ * @private
*/
-SwipeView.prototype.hide = function() {
+SwipeView.prototype.hide_ = function() {
+ this.adapter_.setGestureEditingInProgress(false);
+ this.candidateView_.hideTooltip();
+ goog.style.setElementShown(this.ripple_, false);
this.armed_ = false;
this.trackElements_ = [];
this.tracking_ = false;
+ this.eventIdentifier_ = undefined;
if (this.triggeredBy) {
this.triggeredBy.setHighlighted(false);
}
+ this.triggeredBy = undefined;
goog.style.setElementShown(this.getElement(), false);
goog.style.setElementShown(this.coverElement_, false);
this.highlightIndex_ = SwipeView.INVALID_INDEX_;
+ goog.dom.classlist.removeAll(this.getElement(), [
+ Css.SWIPE_ACTIVE, Css.SELECTION_TRACK, Css.DELETION_TRACK,
+ Css.LEFT_TO_RIGHT
+ ]);
+};
+
+
+/**
+ * Records statistics from this swipe.
+ *
+ * @private
+ */
+SwipeView.prototype.recordStatistics_ = function() {
+ if (this.isSelection_) {
+ this.statistics_.recordValue(
+ 'InputMethod.VirtualKeyboard.BackwardsMovesPerSwipe',
+ this.forwardMoves_, 100, 50);
+ this.statistics_.recordValue(
+ 'InputMethod.VirtualKeyboard.MovesPerSwipe', this.backwardMoves_,
+ 100, 50);
+ } else {
+ this.statistics_.recordValue(
+ 'InputMethod.VirtualKeyboard.WordsDeletedPerSwipe', this.forwardMoves_,
+ 100, 50);
+ this.statistics_.recordValue(
+ 'InputMethod.VirtualKeyboard.WordsRestoredPerSwipe',
+ this.backwardMoves_, 100, 50);
+ }
+ this.forwardMoves_ = 0;
+ this.backwardMoves_ = 0;
};
@@ -687,26 +1072,38 @@ SwipeView.prototype.isAtOrigin = function() {
/**
- * Highlights the item according to the current coordinate of the finger.
+ * Swipes to the coordinates specified on the track.
*
* @param {number} x .
* @param {number} y .
- * @return {boolean} Whether it passed into a new segment.
+ * @return {SwipeDirection|undefined} Direction swiped else undefined if there
+ * there was no change.
*/
-SwipeView.prototype.highlightItem = function(x, y) {
+SwipeView.prototype.swipeOnTrack = function(x, y) {
var previousIndex = this.highlightIndex_;
for (var i = 0; i < this.trackElements_.length; i++) {
var elem = this.trackElements_[i];
var coordinate = goog.style.getClientPosition(elem);
var size = goog.style.getSize(elem);
- if (coordinate.x < x && (coordinate.x + size.width) > x) {
+ var visible = (goog.style.getComputedStyle(elem, 'display') != 'none');
+ if (visible && coordinate.x < x && (coordinate.x + size.width) > x) {
this.highlightIndex_ = i;
this.clearAllHighlights_();
this.setElementBackground_(elem, true);
+ goog.dom.classlist.add(this.getElement(), Css.SWIPE_ACTIVE);
}
}
- this.tracking_ = this.tracking_ || (previousIndex != this.highlightIndex_);
- return (previousIndex != this.highlightIndex_);
+ var changed = previousIndex != this.highlightIndex_;
+ this.tracking_ = this.tracking_ || changed;
+ // Update finger affordance.
+ goog.style.setPosition(this.fingerTracker_,
+ x - (SwipeView.FINGER_TRACKER_WIDTH_ / 2),
+ y - (SwipeView.FINGER_TRACKER_WIDTH_ / 2));
+ if (!changed || previousIndex == SwipeView.INVALID_INDEX_) {
+ return undefined;
+ }
+ return previousIndex < this.highlightIndex_ ?
+ SwipeDirection.RIGHT : SwipeDirection.LEFT;
};
@@ -715,8 +1112,7 @@ SwipeView.prototype.highlightItem = function(x, y) {
*
* @private
*/
-SwipeView.prototype.clearAllHighlights_ =
- function() {
+SwipeView.prototype.clearAllHighlights_ = function() {
for (var i = 0; i < this.trackElements_.length; i++) {
this.setElementBackground_(this.trackElements_[i], false);
}
@@ -733,11 +1129,9 @@ SwipeView.prototype.clearAllHighlights_ =
SwipeView.prototype.setElementBackground_ =
function(element, highlight) {
if (highlight) {
- goog.dom.classlist.add(element, i18n.input.chrome.inputview.Css
- .ELEMENT_HIGHLIGHT);
+ goog.dom.classlist.add(element, Css.ELEMENT_HIGHLIGHT);
} else {
- goog.dom.classlist.remove(element, i18n.input.chrome.inputview.Css
- .ELEMENT_HIGHLIGHT);
+ goog.dom.classlist.remove(element, Css.ELEMENT_HIGHLIGHT);
}
};
@@ -760,6 +1154,7 @@ SwipeView.prototype.addKey_ = function(opt_character, opt_icon_css) {
} else {
keyElem = dom.createDom(goog.dom.TagName.DIV, Css.SWIPE_KEY);
}
+ goog.dom.classlist.add(keyElem, Css.SWIPE_PIECE);
if (opt_icon_css) {
var child = dom.createDom(goog.dom.TagName.DIV, opt_icon_css);
dom.appendChild(keyElem, child);
@@ -779,12 +1174,11 @@ SwipeView.prototype.addKey_ = function(opt_character, opt_icon_css) {
*/
SwipeView.prototype.addSeparator_ = function(width, height) {
var dom = this.getDomHelper();
- var tableCell = dom.createDom(goog.dom.TagName.DIV,
- i18n.input.chrome.inputview.Css.TABLE_CELL);
- goog.style.setSize(tableCell, width + 'px', height + 'px');
- goog.dom.classlist.add(tableCell, Css.SWIPE_SEPARATOR);
- dom.appendChild(this.getElement(), tableCell);
- return tableCell;
+ var separator = dom.createDom(goog.dom.TagName.DIV, Css.SWIPE_SEPARATOR);
+ goog.style.setSize(separator, width + 'px', height + 'px');
+ goog.dom.classlist.add(separator, Css.SWIPE_PIECE);
+ dom.appendChild(this.getElement(), separator);
+ return separator;
};
@@ -814,10 +1208,31 @@ SwipeView.prototype.getHighlightedIndex = function() {
};
+/**
+ * Returns whether gesture deletion is enabled for the current context.
+ *
+ * @return {boolean}
+ */
+SwipeView.prototype.isDeletionEnabled = function() {
+ // TODO: Omni bar sends wrong anchor/focus when autocompleting
+ // URLs. Re-enable when that is fixed.
+ if (this.adapter_.contextType == ContextType.URL) {
+ return false;
+ }
+ if (this.adapter_.isA11yMode) {
+ return false;
+ }
+ if (!this.isKeysetSupported_) {
+ return false;
+ }
+ // TODO: Disable if the current layout is emoji or handwriting.
+ return this.enabled;
+};
+
+
/** @override */
SwipeView.prototype.resize = function(width, height) {
goog.base(this, 'resize', width, height);
-
goog.style.setSize(this.coverElement_, width, height);
};
@@ -829,7 +1244,7 @@ SwipeView.prototype.resize = function(width, height) {
SwipeView.prototype.reset = function() {
this.deletedWords_ = [];
this.surroundingText_ = '';
- this.hide();
+ this.hide_();
};

Powered by Google App Engine
This is Rietveld 408576698