/*
* Copyright 2016 cruxframework.org.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.cruxframework.crux.smartfaces.client.slider;
import java.util.Date;
import org.cruxframework.crux.core.client.css.transition.Transition;
import org.cruxframework.crux.core.client.event.SelectEvent;
import org.cruxframework.crux.smartfaces.client.slider.SlideOutPanel.MenuOrientation;
import org.cruxframework.crux.smartfaces.client.slider.SlideOutPanel.SlideOutPanelEventHandlers;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEndHandler;
import com.google.gwt.event.dom.client.TouchEvent;
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.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Timer;
/**
*
* @author Thiago da Rosa de Bustamante
*
*/
class SlideOutPanelTouchEventHandlers extends SlideOutPanelEventHandlers
implements TouchStartHandler, TouchMoveHandler, TouchEndHandler
{
private static final int SWIPE_THRESHOLD = 50;
private static final long SWIPE_TIME_THRESHOLD = 250;
private int currentTouchPosition;
private boolean didMove;
private int menuPanelHeight;
private int menuPanelWidth;
private int startTouchPosition;
private long startTouchTime;
private HandlerRegistration touchEndHandler;
private HandlerRegistration touchMoveHandler;
private HandlerRegistration touchStartHandler;
@Override
public void onTouchEnd(TouchEndEvent event)
{
event.stopPropagation();
// maybeStopEvent(event);
if (slideOutPanel.sliding)
{
return;
}
resetHandlers();
final boolean eventTargetsMenu = eventTargetsMenu(event.getNativeEvent());
//TODO: check this! Giving some time to onTouchEnd finalize.
//If we don't do it, focus events that runs inside any selectHandler
//will be overridden by (some crazy?) focus events onTouchEnd.
new Timer()
{
@Override
public void run()
{
if (slideOutPanel.slideEnabled)
{
if (currentTouchPosition != startTouchPosition)
{
final int slideBy = getSlideBy();
slideOutPanel.slide(slideBy, false, slideBy != 0);
}
else
{
SlideEndEvent.fire(slideOutPanel);
}
}
if (!didMove)
{
if (!eventTargetsMenu)
{
SelectEvent.fire(slideOutPanel);
}
}
}
}.schedule(50);
}
@Override
public void onTouchMove(TouchMoveEvent event)
{
if (slideOutPanel.sliding)
{
return;
}
if (slideOutPanel.isHorizontalOrientation())
{
handleHorizontalTouchMove(event);
}
else
{
handleVerticalTouchMove(event);
}
}
@Override
public void onTouchStart(TouchStartEvent event)
{
didMove = false;
SlideStartEvent.fire(slideOutPanel);
startTouchPosition = slideOutPanel.isHorizontalOrientation()?event.getTouches().get(0).getClientX():event.getTouches().get(0).getClientY();
currentTouchPosition = startTouchPosition;
startTouchTime = new Date().getTime();
touchMoveHandler = slideOutPanel.addTouchMoveHandler(this);
touchEndHandler = slideOutPanel.addTouchEndHandler(this);
menuPanelWidth = slideOutPanel.menuPanel.getElement().getOffsetWidth();
menuPanelHeight = slideOutPanel.menuPanel.getElement().getOffsetHeight();
maybeStopEvent(event);
}
@Override
public void releaseEvents()
{
if (touchStartHandler != null)
{
touchStartHandler.removeHandler();
touchStartHandler = null;
}
super.releaseEvents();
}
@Override
protected void handleEvents(final SlideOutPanel slideOutPanel)
{
super.handleEvents(slideOutPanel);
touchStartHandler = slideOutPanel.addTouchStartHandler(this);
}
private int getHorizontalSlideBy()
{
int slideBy;
int distX = currentTouchPosition - startTouchPosition;
int width = slideOutPanel.menuPanel.getElement().getOffsetWidth();
if (isSwapEvent(distX))
{
slideBy = distX > 0?width:-width;
}
else
{
if (Math.abs(distX) > width / 2)
{
slideBy = (distX > 0) ? width : width * -1;
}
else if (slideOutPanel.open)
{
slideBy = menuPanelWidth;
}
else
{
slideBy = 0;
}
}
if ((slideBy > 0 && slideOutPanel.menuOrientation == MenuOrientation.right)
|| (slideBy < 0 && slideOutPanel.menuOrientation == MenuOrientation.left))
{
slideBy = 0;
}
return slideBy;
}
/**
* return the final width used to slide the panels.
* @param hasPreviousPanel
* @param hasNextPanel
* @return negative width means "go to next", positive "go to previous" and zero "keep on current"
*/
private int getSlideBy()
{
if (slideOutPanel.isHorizontalOrientation())
{
return getHorizontalSlideBy();
}
return getVerticalSlideBy();
}
private int getVerticalSlideBy()
{
int slideBy;
int distY = currentTouchPosition - startTouchPosition;
int height = slideOutPanel.menuPanel.getElement().getOffsetHeight();
if (isSwapEvent(distY))
{
slideBy = distY > 0?height:-height;
}
else
{
if (Math.abs(distY) > height / 2)
{
slideBy = (distY > 0) ? height : height * -1;
}
else if (slideOutPanel.open)
{
slideBy = menuPanelHeight;
}
else
{
slideBy = 0;
}
}
if ((slideBy > 0 && slideOutPanel.menuOrientation == MenuOrientation.bottom)
|| (slideBy < 0 && slideOutPanel.menuOrientation == MenuOrientation.top))
{
slideBy = 0;
}
return slideBy;
}
private void handleHorizontalTouchMove(TouchMoveEvent event)
{
int clientX = event.getTouches().get(0).getClientX();
int diff = clientX - startTouchPosition;
int absDiff = Math.abs(diff);
if (!didMove && (absDiff > slideOutPanel.slideSensitivity))
{
didMove = true;
}
if (absDiff < menuPanelWidth)
{
int delta = diff;
if (slideOutPanel.open)
{
delta += slideOutPanel.menuOrientation == MenuOrientation.left?menuPanelWidth:-menuPanelWidth;
}
// check boundaries
if ((slideOutPanel.menuOrientation == MenuOrientation.right && delta < 0 && delta > -menuPanelWidth) ||
(slideOutPanel.menuOrientation == MenuOrientation.left && delta > 0 && delta < menuPanelWidth))
{
currentTouchPosition = clientX;
if (slideOutPanel.slideEnabled && didMove)
{
Transition.translateX(slideOutPanel.mainPanel, delta, null);
}
}
}
}
private void handleVerticalTouchMove(TouchMoveEvent event)
{
int clientY = event.getTouches().get(0).getClientY();
int diff = clientY - startTouchPosition;
int absDiff = Math.abs(diff);
if (!didMove && (absDiff > slideOutPanel.slideSensitivity))
{
didMove = true;
}
if (absDiff < menuPanelHeight)
{
int delta = diff;
if (slideOutPanel.open)
{
delta += slideOutPanel.menuOrientation == MenuOrientation.top?menuPanelHeight:-menuPanelHeight;
}
// check boundaries
if ((slideOutPanel.menuOrientation == MenuOrientation.bottom && delta < 0 && delta > -menuPanelHeight) ||
(slideOutPanel.menuOrientation == MenuOrientation.top && delta > 0 && delta < menuPanelHeight))
{
currentTouchPosition = clientY;
if (slideOutPanel.slideEnabled && didMove)
{
Transition.translateY(slideOutPanel.mainPanel, delta, null);
}
}
}
}
private boolean isSwapEvent(int distX)
{
long endTime = new Date().getTime();
long diffTime = endTime - this.startTouchTime;
if (diffTime <= SWIPE_TIME_THRESHOLD)
{
if (Math.abs(distX) >= SWIPE_THRESHOLD)
{
return true;
}
}
return false;
}
private void maybeStopEvent(TouchEvent<?> event)
{
if (slideOutPanel.stopPropagationTouchEvents)
{
event.stopPropagation();
}
if (slideOutPanel.preventDefaultTouchEvents)
{
event.preventDefault();
}
}
private void resetHandlers()
{
if(touchMoveHandler != null)
{
touchMoveHandler.removeHandler();
touchMoveHandler = null;
}
if(touchEndHandler != null)
{
touchEndHandler.removeHandler();
touchEndHandler = null;
}
}
}