GWT ScrollPanel for Touch Screens

Getting this working is easier than you think. Basically the only tricky bit is that the touch object allows for multiple fingers they are represented in the array ‘touches[]’. Since we are only scrolling we only need to worry about the first finger which is why I use touches[0].screenY. Enjoy.

package com.peterfranza.gwt.mobileui.client.widgets;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.ui.ScrollPanel;

/**
 *
 * @author peter.franza
 *
 */
public class TouchScrollPanel extends ScrollPanel {

	private int initialTouchX = -1;
	private int initialTouchY = -1;
	private int initialHorizontalOffset;
	private int initialVerticalOffset;
	private boolean moved = false;

	{
		attachTouch(getElement());
	}

	public TouchScrollPanel(VerticalPanel body) {
		super(body);
	}

	private native void attachTouch(JavaScriptObject ele) /*-{
		var ref = this;
		ele.ontouchstart = function(evt){
	  		evt.preventDefault();
	  		ref.@com.peterfranza.gwt.mobileui.client.widgets.TouchScrollPanel::setInitialTouch(II)(evt.touches[0].screenX, evt.touches[0].screenY);
		}
		ele.ontouchmove = function(evt){
	  		evt.preventDefault();
	  		ref.@com.peterfranza.gwt.mobileui.client.widgets.TouchScrollPanel::doScroll(II)(evt.touches[0].screenX, evt.touches[0].screenY);
		}		
		ele.ontouchend = function(evt){
			evt.preventDefault();
			ref.@com.peterfranza.gwt.mobileui.client.widgets.TouchScrollPanel::setEndTouch(II)(evt.pageX, evt.pageY);
		}		
	}-*/;

	private native void fireClick(int x, int y) /*-{
		var theTarget = $doc.elementFromPoint(x, y);
		if (theTarget.nodeType == 3) theTarget = theTarget.parentNode;

		var theEvent = $doc.createEvent('MouseEvents');
		theEvent.initEvent('click', true, true);
		theTarget.dispatchEvent(theEvent);
	}-*/;


	@SuppressWarnings("unused")
	private void setInitialTouch(int x, int y) {
		initialVerticalOffset = getScrollPosition();
		initialHorizontalOffset = getHorizontalScrollPosition();

		initialTouchX = x;
		initialTouchY = y;
		moved = false;

	}

	@SuppressWarnings("unused")
	private void doScroll(int x, int y) {
		if (initialTouchY != -1) {
			moved = true;
			int vDelta = initialTouchY - y;
			int hDelta = initialTouchX - x;
			setScrollPosition(vDelta + initialVerticalOffset);
			setHorizontalScrollPosition(hDelta + initialHorizontalOffset);
		}
	}

	@SuppressWarnings("unused")
	private void setEndTouch(int x, int y) {
		if (!moved) {
			fireClick(x, y);
		}
		initialTouchX = -1;
		initialTouchY = -1;
	}
}