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 43836e4eec452450fa8973a7c1856a9825b8366a..a8ee7491358c30dc183a8715efded41bc9d9cfcd 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 |
@@ -16,33 +16,32 @@ 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.Transition'); |
goog.require('goog.fx.dom.FadeOut'); |
goog.require('goog.fx.dom.PredefinedEffect'); |
goog.require('goog.fx.easing'); |
+goog.require('goog.log'); |
goog.require('goog.style'); |
+goog.require('i18n.input.chrome.ElementType'); |
goog.require('i18n.input.chrome.Statistics'); |
+goog.require('i18n.input.chrome.events.KeyCodes'); |
goog.require('i18n.input.chrome.inputview.Css'); |
goog.require('i18n.input.chrome.inputview.SwipeDirection'); |
goog.require('i18n.input.chrome.inputview.elements.Element'); |
-goog.require('i18n.input.chrome.inputview.elements.ElementType'); |
goog.require('i18n.input.chrome.inputview.events.EventType'); |
-goog.require('i18n.input.chrome.inputview.events.KeyCodes'); |
goog.require('i18n.input.chrome.inputview.handler.PointerHandler'); |
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 ElementType = i18n.input.chrome.ElementType; |
var EventType = i18n.input.chrome.inputview.events.EventType; |
-var KeyCodes = i18n.input.chrome.inputview.events.KeyCodes; |
+var KeyCodes = i18n.input.chrome.events.KeyCodes; |
var SwipeDirection = i18n.input.chrome.inputview.SwipeDirection; |
var content = i18n.input.chrome.inputview.elements.content; |
-var util = i18n.input.chrome.inputview.util; |
@@ -89,20 +88,12 @@ i18n.input.chrome.inputview.elements.content.SwipeView = function( |
this.surroundingText_ = ''; |
/** |
- * The ending position of the selection in the surrounding text. This value |
- * indicates caret position if there is no selection. |
+ * The offset position of the surrounding text. This value |
+ * indicates the absolute position of the first character of surrounding text. |
* |
* @private {number} |
*/ |
- this.surroundingTextFocus_ = 0; |
- |
- /** |
- * The beginning position of the selection in the surrounding text. This value |
- * indicates current caret position if there is no selection. |
- * |
- * @private {number} |
- */ |
- this.surroundingTextAnchor_ = 0; |
+ this.surroundingTextOffset_ = 0; |
/** |
* List of recent words that have been deleted in the order that they |
@@ -225,7 +216,6 @@ i18n.input.chrome.inputview.elements.content.SwipeView = function( |
*/ |
this.backwardMoves_ = 0; |
- |
/** |
* Whether the current track is selection or deletion. |
* |
@@ -233,13 +223,41 @@ i18n.input.chrome.inputview.elements.content.SwipeView = function( |
*/ |
this.isSelection_ = true; |
- |
/** |
* Triggering event identifier. |
* |
* @private {number|undefined} |
*/ |
this.eventIdentifier_; |
+ |
+ |
+ /** |
+ * Relative surrounding text when the deletion track first shows. |
+ * |
+ * @private {!string} |
+ */ |
+ this.initialSurroundingText_ = ''; |
+ |
+ /** |
+ * Total surrounding text length when the deletion track first shows. |
+ * |
+ * @private {!number} |
+ */ |
+ this.initialSurroundingTextLength_ = 0; |
+ |
+ /** |
+ * Number of deletion noops. |
+ * |
+ * @private {!number} |
+ */ |
+ this.noopCount_ = 0; |
+ |
+ /** |
+ * Logger for SwipeView. |
+ * @private {goog.log.Logger} |
+ */ |
+ this.logger_ = goog.log.getLogger( |
+ 'i18n.input.chrome.inputview.elements.content.SwipeView'); |
}; |
goog.inherits(i18n.input.chrome.inputview.elements.content.SwipeView, |
i18n.input.chrome.inputview.elements.Element); |
@@ -284,15 +302,6 @@ 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  . |
* |
* @private {string} |
@@ -379,50 +388,60 @@ SwipeView.prototype.isArmed = function() { |
SwipeView.prototype.onSurroundingTextChanged_ = function(e) { |
if (this.adapter_.isPasswordBox()) { |
this.surroundingText_ = ''; |
- this.surroundingTextAnchor_ = 0; |
- this.surroundingTextFocus_ = 0; |
+ this.surroundingTextOffset_ = 0; |
return; |
} |
- this.surroundingTextAnchor_ = e.anchor; |
- this.surroundingTextFocus_ = e.focus; |
- var text = e.text || ''; |
+ // Extract text before the cursor. |
+ var text = e.textBeforeCursor || ''; |
+ if (this.surroundingText_ == text && |
+ this.surroundingTextOffset_ == e.offset) { |
+ // Duplicate event. |
+ return; |
+ } |
+ // Cache old values. |
var oldText = this.surroundingText_; |
- var diff = ''; |
- if (oldText == text) { |
- console.error('Duplicate surrounding text event.'); |
+ var oldOffset = this.surroundingTextOffset_; |
+ // Update stored values. |
+ this.surroundingTextOffset_ = e.offset; |
+ this.surroundingText_ = text; |
+ // Check for selection in progress. |
+ if (e.anchor != e.focus) { |
return; |
} |
- if (util.isLetterDelete(oldText, text)) { |
- diff = oldText.slice(-1); |
- // Check if the transformation from oldtext to text was a single letter being |
- // restored. |
- } else if (util.isLetterRestore(oldText, text)) { |
+ var diff = ''; |
+ var delta = (oldText.length + oldOffset) - |
+ (text.length + e.offset); |
+ if (delta > 0) { |
+ // Deletion occurred. |
+ if (delta <= oldText.length) { |
+ diff = oldText.slice(-delta); |
+ } else { |
+ // First OSTC event, ignore. |
+ return; |
+ } |
+ } else if (delta < 0) { |
+ // Text inserted. |
// Handle blink bug where ctrl+delete deletes a space and inserts |
// a  . |
// Convert   to ' ' and remove from delete words since blink |
// did a minirestore for us. |
- var letter = text[text.length - 1]; |
- if (letter == SwipeView.NBSP_CHAR_ || |
- letter == ' ') { |
+ var restored = text.slice(delta); |
+ var letter = restored[0]; |
+ if (letter == SwipeView.NBSP_CHAR_) { |
var lastDelete = this.deletedWords_.pop(); |
- var firstChar = (lastDelete && lastDelete[0]) || ''; |
- if (firstChar == SwipeView.NBSP_CHAR_ || |
- firstChar == ' ') { |
- this.deletedWords_.push(lastDelete.slice(1)); |
+ if (lastDelete) { |
+ var firstChar = lastDelete[0] || ''; |
+ if (firstChar != ' ') { |
+ // Not in the edge case mentioned, restore the deletion. |
+ this.deletedWords_.push(lastDelete); |
+ } else { |
+ // First character was the extra ' '. |
+ this.deletedWords_.push(lastDelete.slice(1)); |
+ } |
} |
} |
- // The current surrounding text may have been cut off since it exceeds |
- // the maximum surrounding text length. |
- } else if (e.text.length == SwipeView.MAX_SURROUNDING_TEXT_LENGTH_ || |
- oldText.length == SwipeView.MAX_SURROUNDING_TEXT_LENGTH_) { |
- // Check if a word was deleted from oldText. |
- var candidate = oldText.trim().split(' ').pop(); |
- if (util.isPossibleDelete(oldText, text, candidate)) { |
- var location = oldText.lastIndexOf(candidate); |
- diff = oldText.slice(location); |
- } |
} else { |
- diff = oldText.substring(text.length); |
+ goog.log.warning(this.logger_, 'Unexpected OSTC event.'); |
} |
if (diff) { |
this.deletedWords_.push(diff); |
@@ -430,7 +449,31 @@ SwipeView.prototype.onSurroundingTextChanged_ = function(e) { |
} else if (!this.isVisible()) { |
this.deletedWords_ = []; |
} |
- this.surroundingText_ = text; |
+}; |
+ |
+ |
+/** |
+ * Attempts to restore the original text input. |
+ * |
+ * @private |
+ * @return {boolean} Whether it was successful. |
+ */ |
+SwipeView.prototype.restoreOriginalText_ = function() { |
+ var restoreLength = (this.initialSurroundingTextLength_) - |
+ (this.surroundingText_.length + this.surroundingTextOffset_); |
+ // Native undo does not work well with composition text. First try to |
+ // compute the delta between what the text was when gesture deletion was |
+ // triggered, and what it is now. |
+ if (restoreLength > 0 && |
+ restoreLength <= this.initialSurroundingText_.length) { |
+ this.adapter_.commitText( |
+ this.initialSurroundingText_.slice(-restoreLength)); |
+ // Prevent using this again. |
+ this.initialSurroundingText_ = ''; |
+ this.initialSurroundingTextLength_ = 0; |
+ return true; |
+ } |
+ return false; |
}; |
@@ -474,31 +517,47 @@ SwipeView.prototype.swipeToDelete_ = function(e) { |
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 |
- }); |
+ if (this.surroundingText_ == '') { |
+ // Empty text, nothing to delete. |
+ this.noopCount_++; |
+ } else { |
+ this.adapter_.sendKeyDownAndUpEvent( |
+ '\u0008', KeyCodes.BACKSPACE, undefined, undefined, { |
+ ctrl: true, |
+ shift: false |
+ }); |
+ } |
} |
} 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); |
+ if (!this.restoreOriginalText_() && this.noopCount_ == 0) { |
+ // Unable to use onSurroundingText, as long as there are no noop undos |
+ // use a native undo for the final undo. |
+ this.adapter_.sendKeyDownAndUpEvent( |
+ '\u007a', KeyCodes.KEY_Z, undefined, undefined, { |
+ ctrl: true, |
+ shift: false |
+ }); |
} |
+ this.noopCount_ = 0; |
break; |
} |
+ if (this.noopCount_ > 0) { |
+ this.noopCount_--; |
+ } else { |
+ this.adapter_.sendKeyDownAndUpEvent( |
+ '\u007a', KeyCodes.KEY_Z, undefined, undefined, { |
+ ctrl: true, |
+ shift: false |
+ }); |
+ } |
} |
} else { |
- console.error('Unexpected swipe direction: ' + direction); |
+ goog.log.warning(this.logger_, 'Unexpected swipe direction: ' + direction); |
} |
}; |
@@ -521,8 +580,6 @@ SwipeView.prototype.setKeysetSupported = function(supported) { |
* @private |
*/ |
SwipeView.prototype.swipeToSelect_ = function(e) { |
- // Cache whether we were tracking as highlight may change this. |
- var alreadyTracking = this.tracking_; |
var previousIndex = this.getHighlightedIndex(); |
var direction = this.swipeOnTrack(e.x, e.y); |
// Swipe did not change track index, ignore. |
@@ -531,7 +588,7 @@ SwipeView.prototype.swipeToSelect_ = function(e) { |
} |
var index = this.getHighlightedIndex(); |
if (index == -1) { |
- console.error('Invalid track index.'); |
+ goog.log.warning(this.logger_, 'Invalid track index.'); |
return; |
} |
// TODO: Set selectWord to true if the shift key is currently pressed. |
@@ -542,14 +599,14 @@ SwipeView.prototype.swipeToSelect_ = function(e) { |
} else if (direction & SwipeDirection.RIGHT) { |
code = KeyCodes.ARROW_RIGHT; |
} else { |
- console.error('Unexpected swipe direction: ' + direction); |
+ goog.log.warning(this.logger_, 'Unexpected swipe direction: ' + direction); |
return; |
} |
// 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.'); |
+ goog.log.warning(this.logger_, 'Swipe index did not change.'); |
} |
if (this.ltr && code == KeyCodes.ARROW_LEFT || |
!this.ltr && code == KeyCodes.ARROW_RIGHT) { |
@@ -598,12 +655,15 @@ SwipeView.prototype.handleSwipeAction_ = function(e) { |
// finger movement. |
return; |
} |
- if (e.direction & i18n.input.chrome.inputview.SwipeDirection.LEFT) { |
+ if (e.direction & SwipeDirection.LEFT) { |
var key = /** @type {!content.FunctionalKey} */ (e.view); |
// Equivalent to a longpress. |
if (this.isDeletionEnabled()) { |
this.showDeletionTrack(key, e.identifier, true); |
} |
+ } else if (e.direction & SwipeDirection.RIGHT) { |
+ this.restoreOriginalText_(); |
+ this.armed_ = false; |
} |
return; |
} |
@@ -630,6 +690,9 @@ SwipeView.prototype.handlePointerAction_ = function(e) { |
if (e.type == EventType.POINTER_DOWN) { |
if (this.adapter_.contextType != ContextType.URL) { |
this.armed_ = true; |
+ this.initialSurroundingText_ = this.surroundingText_; |
+ this.initialSurroundingTextLength_ = |
+ this.surroundingText_.length + this.surroundingTextOffset_; |
} |
this.deletedWords_ = []; |
} else if (e.type == EventType.POINTER_UP || |
@@ -749,7 +812,6 @@ SwipeView.prototype.showDeletionTrack_ = function(x, y, width, height) { |
if (this.ltr) { |
goog.dom.classlist.add(this.getElement(), Css.LEFT_TO_RIGHT); |
} |
- var ltr = this.ltr; |
for (var i = 0; i < SwipeView.LENGTH_; i++) { |
var keyElem = this.addKey_(); |
goog.style.setSize(keyElem, width, height); |
@@ -779,6 +841,12 @@ SwipeView.prototype.showDeletionTrack_ = function(x, y, width, height) { |
var elem = this.trackElements_[this.highlightIndex_]; |
this.setElementBackground_(elem, true); |
} |
+ if (this.adapter_.contextType == ContextType.NUMBER || |
+ this.adapter_.contextType == ContextType.PHONE) { |
+ goog.dom.classlist.add(this.coverElement_, Css.NUMERIC_LAYOUT); |
+ } else { |
+ goog.dom.classlist.remove(this.coverElement_, Css.NUMERIC_LAYOUT); |
+ } |
goog.style.setElementShown(this.coverElement_, true); |
this.triggeredBy && this.triggeredBy.setHighlighted(true); |
}; |
@@ -839,7 +907,7 @@ SwipeView.prototype.showSelectionTrack_ = function(x, y, width, height) { |
keyElem = this.addSeparator_(width, height); |
goog.style.setSize(keyElem, width, height); |
- this.trackElements_.push(keyElem); |
+ this.trackElements_.push(keyElem || undefined); |
if (this.ltr) { |
keyElem = this.addKey_(); |
@@ -862,6 +930,12 @@ SwipeView.prototype.showSelectionTrack_ = function(x, y, width, height) { |
'top': y |
}); |
} |
+ if (this.adapter_.contextType == ContextType.NUMBER || |
+ this.adapter_.contextType == ContextType.PHONE) { |
+ goog.dom.classlist.add(this.coverElement_, Css.NUMERIC_LAYOUT); |
+ } else { |
+ goog.dom.classlist.remove(this.coverElement_, Css.NUMERIC_LAYOUT); |
+ } |
goog.style.setElementShown(this.coverElement_, true); |
this.triggeredBy && this.triggeredBy.setHighlighted(true); |
}; |
@@ -973,7 +1047,7 @@ ScaleAtPoint.prototype.updateStyle = function() { |
*/ |
SwipeView.prototype.onFadeStarted_ = function() { |
goog.events.unlisten(this.fadeAnimation_, |
- goog.fx.Animation.EventType.BEGIN, |
+ goog.fx.Transition.EventType.BEGIN, |
this.onFadeStarted_); |
this.scaleAnimation_.play(); |
}; |
@@ -990,7 +1064,7 @@ 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, |
+ goog.events.listen(this.fadeAnimation_, goog.fx.Transition.EventType.BEGIN, |
this.onFadeStarted_.bind(this)); |
this.fadeAnimation_.play(); |
}; |
@@ -1020,6 +1094,9 @@ SwipeView.prototype.hide_ = function() { |
this.trackElements_ = []; |
this.tracking_ = false; |
this.eventIdentifier_ = undefined; |
+ this.noopCount_ = 0; |
+ this.initialSurroundingText_ = ''; |
+ this.initialSurroundingTextLength_ = 0; |
if (this.triggeredBy) { |
this.triggeredBy.setHighlighted(false); |
} |
@@ -1219,6 +1296,13 @@ SwipeView.prototype.isDeletionEnabled = function() { |
if (this.adapter_.contextType == ContextType.URL) { |
return false; |
} |
+ // TODO(rsadam): Re-enable when ctrl+z is fixed on gmail. |
+ if (this.adapter_.isGoogleMail()) { |
+ return false; |
+ } |
+ if (this.adapter_.isPasswordBox()) { |
+ return false; |
+ } |
if (this.adapter_.isA11yMode) { |
return false; |
} |