package org.vaadin.touchkit.gwt.client.ui;
import java.util.Date;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.HumanInputEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEndHandler;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchMoveHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.TouchScrollDelegate;
public class VSwipeView extends SimplePanel {
public interface SwipeListener {
void onSwipeBack();
void onSwipeForward();
}
private SwipeListener swipeListener;
private static final double SPEED_THRESHOLD = 0.35;
private static final String CLASSNAME = "v-touchkit-swipeview";
private int dragstartX;
private int dragstartY;
private boolean dragging;
private boolean swiping;
private long lastTs;
private int lastX;
private double lastSpeed;
private boolean touchDrag;
private VNavigationManager np;
private Element scrollElement;
protected TouchStartEvent dragStartEvent;
private TouchScrollDelegate touchScrollDelegate;
private boolean enabled = true;
public VSwipeView() {
setStyleName(CLASSNAME);
getElement().getStyle().setProperty("webkitUserSelect", "none");
/*
* Touch scrolling using one finger handled by TouchScrollDelegate.
*/
sinkEvents(Event.TOUCHEVENTS);
scrollElement = getElement();
Style style = scrollElement.getStyle();
style.setOverflow(Overflow.AUTO);
style.setHeight(100, Unit.PCT);
style.setPosition(Position.ABSOLUTE);
sinkEvents(Event.MOUSEEVENTS);
DOM.sinkEvents(scrollElement, Event.ONSCROLL);
touchScrollDelegate = new TouchScrollDelegate(scrollElement);
initHandlers();
}
protected void initHandlers() {
addHandler(new TouchStartHandler() {
public void onTouchStart(TouchStartEvent event) {
dragStartEvent = event;
dragStart(event);
}
}, TouchStartEvent.getType());
addHandler(new MouseDownHandler() {
public void onMouseDown(MouseDownEvent event) {
if (event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
dragStart(event);
}
}
}, MouseDownEvent.getType());
addHandler(new MouseMoveHandler() {
public void onMouseMove(MouseMoveEvent event) {
dragMove(event);
}
}, MouseMoveEvent.getType());
addHandler(new TouchMoveHandler() {
public void onTouchMove(TouchMoveEvent event) {
dragMove(event);
}
}, TouchMoveEvent.getType());
addHandler(new MouseUpHandler() {
public void onMouseUp(MouseUpEvent event) {
dragEnd(event);
}
}, MouseUpEvent.getType());
addHandler(new TouchEndHandler() {
public void onTouchEnd(TouchEndEvent event) {
dragEnd(event);
}
}, TouchEndEvent.getType());
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
protected void dragStart(HumanInputEvent event) {
NativeEvent ne = event.getNativeEvent();
VConsole.log("Drag start" + ne.getType());
if (!dragging && np != null && isEnabled()) {
dragging = true;
touchDrag = Event.as(ne).getTypeInt() == Event.ONTOUCHSTART;
dragstartX = Util.getTouchOrMouseClientX(ne);
dragstartY = Util.getTouchOrMouseClientY(ne);
if (!BrowserInfo.get().isTouchDevice()) {
// avoid drag start on images
// FIXME shouln't be this way, but disables dragstart on images
// in demo with desktop browsers and this way makes development
// easier
Element el = ne.getEventTarget().cast();
String msg = el.getParentElement().getClassName();
if (msg.contains("embedded")) {
ne.preventDefault();
}
}
new Timer() {
@Override
public void run() {
// Swipe must start soon or drag start will be ignored
if(!swiping) {
dragging = false;
}
}
}.schedule(200);;
}
}
@Override
protected void onAttach() {
super.onAttach();
Widget parent2 = getParent();
if (parent2 instanceof VNavigationManager) {
np = (VNavigationManager) parent2;
}
}
protected void dragMove(HumanInputEvent event) {
if (np != null) {
NativeEvent ne = event.getNativeEvent();
if (touchDrag && Event.as(ne).getTypeInt() != Event.ONTOUCHMOVE) {
return;
}
int x = Util.getTouchOrMouseClientX(ne);
int y = Util.getTouchOrMouseClientY(ne);
long time = new Date().getTime();
// screens per second
double screenwidths = (x - lastX) / (double) getOffsetWidth();
double seconds = (time - lastTs) / 100d;
lastSpeed = screenwidths / seconds;
lastX = x;
lastTs = time;
int deltaX = x - dragstartX;
if (swiping) {
VConsole.log("Swipe move " + deltaX);
np.setHorizontalOffset(deltaX, false);
ne.preventDefault(); // prevent page scroll
} else if (dragging) {
Event.setCapture(getElement());
int dragY = dragstartY - y;
if (Math.abs(deltaX / (double) dragY) > 2) {
swiping = true;
np.setHorizontalOffset(deltaX, false);
ne.preventDefault(); // prevent page scroll
}
if (BrowserInfo.get().requiresTouchScrollDelegate()) {
if (Math.abs(deltaX / (double) dragY) < 0.5) {
if (Event.as(event.getNativeEvent()).getTypeInt() == Event.ONTOUCHMOVE) {
/*
* We'll "lazyly" activate touchScrollDelegate if
* the direction is enough down.
*/
dragStartEvent.setNativeEvent(event
.getNativeEvent());
touchScrollDelegate.onTouchStart(dragStartEvent);
VConsole.log("Lazy started");
dragging = false;
}
}
}
}
}
}
public void setScrollTop(final int scrolloffset) {
scrollElement.setScrollTop(scrolloffset);
}
protected void dragEnd(HumanInputEvent event) {
if (dragging) {
Event.releaseCapture(getElement());
VConsole.log("Drag end");
dragging = false;
if (swiping) {
if (np != null) {
NativeEvent ne = event.getNativeEvent();
int x = Util.getTouchOrMouseClientX(ne);
int deltaX = x - dragstartX;
VConsole.log("Speed" + lastSpeed);
if (np.getPreviousView() != null
&& (deltaX > getOffsetWidth() / 2 || lastSpeed > SPEED_THRESHOLD)) {
// navigate backward
np.navigateBackward();
if (swipeListener != null) {
swipeListener.onSwipeBack();
}
} else if (np.getNextView() != null
&& (deltaX < -getOffsetWidth() / 2 || (lastSpeed < -SPEED_THRESHOLD))) {
// navigate forward
np.navigateForward();
if (swipeListener != null) {
swipeListener.onSwipeForward();
}
} else {
np.setHorizontalOffset(0, true);
}
}
swiping = false;
}
}
}
public int getScrollTop() {
return scrollElement.getScrollTop();
}
public SwipeListener getSwipeListener() {
return swipeListener;
}
public void setSwipeListener(SwipeListener swipeListener) {
this.swipeListener = swipeListener;
}
}