Bug 592330 - [VKB] Enhance virtual keyboard support in the chrome UI - part 2 [r=mfinkle,stechz]
authorVivien Nicolas <21@vingtetun.org>
Thu, 07 Oct 2010 23:50:27 +0200
changeset 2159 5524469c8f293b84682fe7761ccf695f361d7f35
parent 2158 f316a2ba79f9af5190a890cc974cbb5c2571bf0a
child 2161 fe98cccac7024c75ebcc402929357d0cf722f9b8
push id1818
push uservnicolas@mozilla.com
push dateThu, 07 Oct 2010 21:50:39 +0000
reviewersmfinkle, stechz
bugs592330
Bug 592330 - [VKB] Enhance virtual keyboard support in the chrome UI - part 2 [r=mfinkle,stechz]
chrome/content/input.js
--- a/chrome/content/input.js
+++ b/chrome/content/input.js
@@ -85,22 +85,21 @@ const kStateActive = 0x00000001;
  *     indicate one last drag movement.
  *
  *   dragMove(dx, dy, scroller)
  *     Signals an input attempt to drag by dx, dy.
  *
  * There is a default dragger in case a scrollable element is dragged --- see
  * the defaultDragger prototype property.
  */
-function MouseModule(owner, browserViewContainer) {
-  this._owner = owner;
-  this._browserViewContainer = browserViewContainer;
+function MouseModule() {
   this._dragData = new DragData(kTapRadius);
 
   this._dragger = null;
+  this._inputField = null;
 
   this._downUpEvents = [];
   this._targetScrollInterface = null;
 
   this._kinetic = new KineticController(this._dragBy.bind(this),
                                         this._kineticStop.bind(this));
 
   this._singleClickTimeout = new Util.Timeout(this._doSingleClick.bind(this));
@@ -215,16 +214,24 @@ MouseModule.prototype = {
     if (dragger) {
       let draggable = dragger.isDraggable(targetScrollbox, targetScrollInterface);
       dragData.locked = !draggable.x || !draggable.y;
       if (draggable.x || draggable.y) {
         this._dragger = dragger;
         this._doDragStart(aEvent);
       }
     }
+
+    // When panning starts over an input field, focus should not change
+    let inputField = this._getTargetInputField(aEvent.originalTarget);
+    if (inputField && this._dragger) {
+      this._inputField = inputField;
+      aEvent.preventDefault();
+      aEvent.stopPropagation();
+    }
   },
 
   /** Send tap up event and any necessary full taps. */
   _onMouseUp: function _onMouseUp(aEvent) {
     this._onMouseMove(aEvent);
 
     let dragData = this._dragData;
     if (dragData.dragging)
@@ -256,16 +263,25 @@ MouseModule.prototype = {
 
     // Do pan
     if (dragData.isPan()) {
       // User was panning around, do not allow click
       let generatesClick = aEvent.detail;
       if (generatesClick)
         aEvent.target.addEventListener("click", this, true);
     }
+
+    // Move the caret to the end of the target input field and focus it
+    if (this._inputField && !this._dragData.isPan()) {
+      let inputField = this._inputField;
+      let textLength = inputField.textLength;
+      inputField.setSelectionRange(textLength, textLength);
+      inputField.focus();
+    }
+    this._inputField = null;
   },
 
   /**
    * If we're in a drag, do what we have to do to drag on.
    */
   _onMouseMove: function _onMouseMove(aEvent) {
     let dragData = this._dragData;
 
@@ -303,16 +319,17 @@ MouseModule.prototype = {
   /** Finish a drag. */
   _doDragStop: function _doDragStop() {
     this._dragData.endDrag();
 
     let dragData = this._dragData;
     if (!dragData.isPan()) {
       // There was no pan, so just stop dragger.
       this._dragger.dragStop(0, 0, this._targetScrollInterface);
+      this._dragger = null;
     } else {
       // Start kinetic pan.
       let [sX, sY] = dragData.panPosition();
       let dX = dragData.prevPanX - sX;
       let dY = dragData.prevPanY - sY;
       this._kinetic.addData(-dX, -dY);
       this._kinetic.start();
     }
@@ -349,16 +366,17 @@ MouseModule.prototype = {
 
   /** Callback for kinetic scroller. */
   _kineticStop: function _kineticStop() {
     // Kinetic panning could finish while user is panning, so don't finish
     // the pan just yet.
     let dragData = this._dragData;
     if (!dragData.dragging) {
       this._dragger.dragStop(0, 0, this._targetScrollInterface);
+      this._dragger = null;
       let event = document.createEvent("Events");
       event.initEvent("PanFinished", true, false);
       document.dispatchEvent(event);
     }
   },
 
   /** Called when tap down times out and becomes a long tap. */
   _doLongClick: function _doLongClick() {
@@ -441,16 +459,32 @@ MouseModule.prototype = {
    * the recorded down/up event queue have been issued out.
    */
   _cleanClickBuffer: function _cleanClickBuffer() {
     this._singleClickTimeout.clear();
     this._longClickTimeout.clear();
     this._downUpEvents.splice(0);
   },
 
+  /* XXXvn this can potentially be moved into ScrollUtils */
+  _getTargetInputField: function _getTargetInputField(aTarget) {
+    let focusedElement = document.commandDispatcher.focusedElement;
+    let parentNode = aTarget.parentNode;
+
+    let inputField = null;
+    if (aTarget.mozIsTextField && aTarget.mozIsTextField(false) && focusedElement != aTarget)
+      inputField = aEventTarget;
+    else if (parentNode.mozIsTextField && parentNode.mozIsTextField(false) && focusedElement != parentNode)
+      inputField = parentNode;
+    else if (aTarget instanceof XULElement && aTarget.inputField)
+      inputField = aTarget.inputField;
+
+    return inputField;
+  },
+
   toString: function toString() {
     return '[MouseModule] {'
       + '\n\tdragData=' + this._dragData + ', '
       + 'dragger=' + this._dragger + ', '
       + '\n\tdownUpEvents=' + this._downUpEvents + ', '
       + 'length=' + this._downUpEvents.length + ', '
       + '\n\ttargetScroller=' + this._targetScrollInterface + '}';
   }