com.google.gwt.query.client.plugins.MousePlugin Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2011, The gwtquery team.
*
* 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 com.google.gwt.query.client.plugins;
import com.google.gwt.core.client.Duration;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.plugins.events.GqEvent;
import com.google.gwt.user.client.Event;
/**
* Base class for all plug-in that need to handle some mouse interactions.
*
*/
public abstract class MousePlugin extends UiPlugin {
private GqEvent startEvent;
private boolean started = false;
private Duration mouseUpDuration;
private MouseOptions options;
private boolean preventClickEvent = false;
private boolean touchSupported = false;
private int startX = -1;
private int startY = -1;
protected MousePlugin(GQuery gq) {
super(gq);
}
protected void destroyMouseHandler() {
as(Events)
.unbind(Event.ONMOUSEDOWN | Event.ONCLICK | Event.ONTOUCHSTART, getPluginName(), null);
}
/**
* Return a String identifying the plugin. This string is used as namespace when we bind handlers.
*
*/
protected abstract String getPluginName();
/**
* This method initialize all needed handlers.
*
*/
protected void initMouseHandler(MouseOptions options) {
this.options = options;
for (final Element e : elements()) {
$(e).as(Events).bind(Event.ONMOUSEDOWN, getPluginName(), (Object) null, new Function() {
@Override
public boolean f(com.google.gwt.user.client.Event event) {
if (touchSupported) {
return true;
}
return mouseDown(e, GqEvent.create(event));
}
}).bind(Event.ONTOUCHSTART, getPluginName(), (Object) null, new Function() {
public boolean f(com.google.gwt.user.client.Event event) {
if (event.getTouches().length() > 1) {
return true;
}
touchSupported = true;
return mouseDown(e, GqEvent.create(event));
}
}).bind(Event.ONCLICK, getPluginName(), (Object) null, new Function() {
@Override
public boolean f(com.google.gwt.user.client.Event event) {
preventClickEvent |= !mouseClick(e, GqEvent.create(event));
if (preventClickEvent) {
preventClickEvent = false;
event.stopPropagation();
event.preventDefault();
return false;
}
return true;
}
});
}
}
/**
* Test if the mouse down event must be handled by the plugin or not.
*/
protected boolean mouseCapture(Element draggable, GqEvent event) {
return true;
}
/**
* Method called when mouse click.
*/
protected boolean mouseClick(Element element, GqEvent event) {
return true;
}
/**
* Method called when mouse down occur on the element.
*
* You should not override this method. Instead, override {@link #mouseStart(Element, GqEvent)}
* method.
*/
protected boolean mouseDown(Element element, GqEvent event) {
// test if an other plugin handle the mouseStart
if (isEventAlreadyHandled(event)) {
return false;
}
if (started) { // case where we missed a mouseup
mouseUp(element, event);
}
// calculate all interesting variables
reset(event);
if (notHandleMouseDown(element, event)) {
return true;
}
if (delayConditionMet() && distanceConditionMet(event)) {
started = mouseStart(element, event);
if (!started) {
event.getOriginalEvent().preventDefault();
return true;
}
}
bindOtherEvents(element);
if (!touchSupported) { // click event are not triggered if we call preventDefault on touchstart event.
event.getOriginalEvent().preventDefault();
}
markEventAsHandled(event);
return true;
}
/**
* Method called when the mouse is dragging.
*/
protected abstract boolean mouseDrag(Element element, GqEvent event);
/**
* Method called on MouseMove event.
*
* You should not override this method. Instead, override {@link #mouseMove(Element, GqEvent)}
* method
*
*/
protected boolean mouseMove(Element element, GqEvent event) {
if (started) {
event.getOriginalEvent().preventDefault();
return mouseDrag(element, event);
}
if (delayConditionMet() && distanceConditionMet(event)) {
started = mouseStart(element, startEvent);
if (started) {
mouseDrag(element, event);
} else {
mouseUp(element, event);
}
}
return !started;
}
/**
* Method called when the mouse is clicked and all conditions for starting the plugin are met.
*
*/
protected abstract boolean mouseStart(Element element, GqEvent event);
/**
* Method called when the mouse button is released.
*/
protected abstract boolean mouseStop(Element element, GqEvent event);
/**
* Method called when mouse is released..
*
* You should not override this method. Instead, override {@link #mouseStop(Element, GqEvent)}
* method.
*/
protected boolean mouseUp(Element element, GqEvent event) {
unbindOtherEvents();
if (started) {
started = false;
preventClickEvent = (event.getCurrentEventTarget() == startEvent.getCurrentEventTarget());
mouseStop(element, event);
}
return true;
}
private void bindOtherEvents(final Element element) {
int moveEvent = touchSupported ? Event.ONTOUCHMOVE : Event.ONMOUSEMOVE;
int endEvents = touchSupported ? Event.ONTOUCHEND : Event.ONMOUSEUP;
$(document).as(Events).bind(moveEvent, getPluginName(), (Object) null, new Function() {
@Override
public boolean f(com.google.gwt.user.client.Event e) {
mouseMove(element, (GqEvent) GqEvent.create(e));
return false;
}
}).bind(endEvents, getPluginName(), (Object) null, new Function() {
@Override
public boolean f(com.google.gwt.user.client.Event e) {
mouseUp(element, (GqEvent) GqEvent.create(e));
return false;
}
});
// TODO Event.ONTOUCHEND | Event.ONTOUCHCANCEL don't work -> investigate
if (touchSupported) {
$(document).as(Events).bind(Event.ONTOUCHCANCEL, getPluginName(), (Object) null,
new Function() {
@Override
public boolean f(com.google.gwt.user.client.Event e) {
mouseUp(element, (GqEvent) GqEvent.create(e));
return false;
}
});
}
}
private boolean delayConditionMet() {
if (mouseUpDuration == null) {
return false;
}
return options.getDelay() <= mouseUpDuration.elapsedMillis();
}
private boolean distanceConditionMet(GqEvent event) {
int neededDistance = options.getDistance();
int xDistance = Math.abs(startX - getClientX(event));
int yDistance = Math.abs(startY - getClientY(event));
// in jQuery-ui we take the greater distance between x and y... not really
// good !
// int mouseDistance = Math.max(xMouseDistance, yMouseDistance);
// use Pythagor theorem !!
int mouseDistance = (int) Math.sqrt(xDistance * xDistance + yDistance * yDistance);
return mouseDistance >= neededDistance;
}
private native boolean isEventAlreadyHandled(GqEvent event) /*-{
var result = event.mouseHandled ? event.mouseHandled : false;
return result;
}-*/;
private native void markEventAsHandled(GqEvent event) /*-{
event.mouseHandled = true;
}-*/;
private boolean notHandleMouseDown(Element element, GqEvent mouseDownEvent) {
boolean isNotBoutonLeft = mouseDownEvent.getButton() != NativeEvent.BUTTON_LEFT;
Element eventTarget = mouseDownEvent.getEventTarget().cast();
boolean isElementCancel = false;
if (options.getCancel() != null) {
isElementCancel =
$(eventTarget).parents().add($(eventTarget)).filter(options.getCancel()).length() > 0;
}
return isNotBoutonLeft || isElementCancel || !mouseCapture(element, mouseDownEvent);
}
private void reset(GqEvent nativeEvent) {
this.startEvent = nativeEvent;
this.startX = getClientX(nativeEvent);
this.startY = getClientY(nativeEvent);
this.mouseUpDuration = new Duration();
}
private void unbindOtherEvents() {
int events =
touchSupported ? Event.ONTOUCHCANCEL | Event.ONTOUCHEND | Event.ONTOUCHMOVE
: Event.ONMOUSEUP | Event.ONMOUSEMOVE;
$(document).as(Events).unbind(events, getPluginName(), null);
}
protected int getClientX(Event e) {
if (touchSupported) {
return e.getTouches().get(0).getClientX();
} else {
return e.getClientX();
}
}
protected int getClientY(Event e) {
if (touchSupported) {
return e.getTouches().get(0).getClientY();
} else {
return e.getClientY();
}
}
}