From: Julien Dramaix Date: Tue, 11 Jan 2011 14:08:14 +0000 (+0000) Subject: common-ui plugin classes moved to the trunk X-Git-Tag: release-1.3.2~593 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=eb9793b44daf5f8b757ec3eec223eb79a2c64a69;p=gwtquery.git common-ui plugin classes moved to the trunk --- diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/GQueryUi.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/GQueryUi.java new file mode 100755 index 00000000..db8d72ea --- /dev/null +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/GQueryUi.java @@ -0,0 +1,246 @@ +package com.google.gwt.query.client.plugins; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.event.shared.GwtEvent; +import com.google.gwt.event.shared.HasHandlers; +import com.google.gwt.query.client.Function; +import com.google.gwt.query.client.GQuery; +import com.google.gwt.query.client.JSArray; +import com.google.gwt.query.client.Plugin; +import com.google.gwt.query.client.Predicate; + +/** + * GWT clone of jQueryUi-core + * + * @author Julien Dramaix (julien.dramaix@gmail.com, @jdramaix) + * + */ +public class GQueryUi extends GQuery { + + /** + * This object allows you to have a full copy of the original Event and + * implements some useful method of the jQuery event model. This is also + * useful in Internet Explorer because it use the same javascript object to + * fire MouseDownEvent, MouseMoveEvent or MouseStopEvent on the same element. + * So, we cannot keep a copy of the MouseDownEvent during a dragginf for + * example. Now we can ! + * + * TOBEFIXED : the method preventDefault() must be called directly on the + * original event + * + * + * @author jdramaix + * + */ + public static class Event extends com.google.gwt.user.client.Event { + + public static Event create(com.google.gwt.user.client.Event originalEvent) { + Event gQueryEvent = createObject().cast(); + copy(originalEvent, gQueryEvent); + return gQueryEvent; + } + + private static native void copy( + com.google.gwt.user.client.Event originalEvent, Event gQueryEvent) /*-{ + for ( var field in originalEvent ) { + gQueryEvent[field] = originalEvent[field]; + } + gQueryEvent.originalEvent = originalEvent; + }-*/; + + protected Event() { + } + + public final native com.google.gwt.user.client.Event getOriginalEvent()/*-{ + return this.originalEvent; + }-*/; + + /** + * The mouse position relative to the left edge of the document + * + * @param mouseEvent + * @return the mouse x-position in the page + */ + public final int pageX() { + return getClientX() + document.getScrollLeft(); + } + + /** + * The mouse position relative to the top edge of the document. + * + * @param mouseEvent + * @return the mouse y-position in the page + */ + public final int pageY() { + return getClientY() + document.getScrollTop(); + } + + /** + * Tell if ctrl or cmd key is pressed + * + * @return + */ + public final boolean isMetaKeyPressed() { + return getMetaKey() || getCtrlKey(); + } + + } + + public static class Dimension { + private int width = 0; + private int height = 0; + + public Dimension(Element e) { + width = e.getOffsetWidth(); + height = e.getOffsetHeight(); + } + + public Dimension(int width, int height) { + this.width = width; + this.height = height; + } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + } + + private static class GQueryUiImpl { + + private final Element getViewportElement() { + return GQuery.document.isCSS1Compat() ? GQuery.document + .getDocumentElement() : GQuery.document.getBody(); + } + + public GQuery scrollParent(final GQueryUi gQueryUi) { + GQuery scrollParent; + + if ("fixed".equals(gQueryUi.css("position"))) { + return GQuery.$(getViewportElement()); + } + + if (scrollParentPositionTest(gQueryUi)) { + scrollParent = gQueryUi.parents().filter(new Predicate() { + + public boolean f(Element e, int index) { + GQuery $e = GQuery.$(e); + String position = $e.css("position", true); + return ("relative".equals(position) || "absolute".equals(position) || "fixed" + .equals(position)) + && isOverflowEnabled($e); + } + }); + + } else { + scrollParent = gQueryUi.parents().filter(new Predicate() { + + public boolean f(Element e, int index) { + return isOverflowEnabled(GQuery.$(e)); + } + }); + } + return scrollParent.length() > 0 ? new GQuery(scrollParent.get(0)) + : GQuery.$(getViewportElement()); + + } + + private boolean isOverflowEnabled(GQuery e) { + String overflow = e.css("overflow", true) + e.css("overflow-x", true) + + e.css("overflow-y", true); + return overflow.contains("auto") || overflow.contains("scroll"); + } + + protected boolean scrollParentPositionTest(GQueryUi gQueryUi) { + return "absolute".equals(gQueryUi.css("position")); + } + + } + + @SuppressWarnings("unused") + private static class GQueryUiImplTrident extends GQueryUiImpl { + + @Override + protected boolean scrollParentPositionTest(GQueryUi gQueryUi) { + String position = gQueryUi.css("position"); + return ("absolute".equals(position) || "relative".equals(position) || "static" + .equals(position)); + } + + } + + public static Class GQueryUi = GQueryUi.class; + + // Register the plugin in GQuery + static { + GQuery.registerPlugin(GQueryUi.class, new Plugin() { + public GQueryUi init(GQuery gq) { + return new GQueryUi(gq); + } + }); + } + + protected HasHandlers eventBus; + + private GQueryUiImpl impl = GWT.create(GQueryUiImpl.class); + + public static boolean contains(Element parent, Element descendant) { + return parent != descendant && parent.isOrHasChild(descendant); + } + + public GQueryUi() { + super(); + } + + public GQueryUi(Element element) { + super(element); + } + + public GQueryUi(GQuery gq) { + super(gq); + } + + public GQueryUi(JSArray elements) { + super(elements); + } + + public GQueryUi(NodeList list) { + super(list); + } + + /** + * this function returns the immediate scrolling parent. + * + * @return the immediate scrolling parent + */ + public GQuery scrollParent() { + return impl.scrollParent(this); + } + + /** + * fire event and call callback function. + * + * @param e + * @param callback + * @param element + */ + protected void trigger(GwtEvent e, Function callback, Element element) { + trigger(e, callback, element, eventBus); + } + + protected static void trigger(GwtEvent e, Function callback, + Element element, HasHandlers handlerManager) { + if (handlerManager != null && e != null) { + handlerManager.fireEvent(e); + } + if (callback != null) { + callback.f(element); + } + } + +} diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MouseOptions.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MouseOptions.java new file mode 100755 index 00000000..9487b7e3 --- /dev/null +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MouseOptions.java @@ -0,0 +1,87 @@ +/* + * Copyright 2010 The gwtquery plugins 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; + +public class MouseOptions { + + /** + * Prevents plugin working from starting on specified elements. + */ + private String[] cancel; + + /** + * Time in milliseconds to define when the plugin should start. It helps + * preventing unwanted selections when clicking on an element. Default : 0 + * + */ + private int delay; + + /** + * Tolerance, in pixels, for when plugin should start. If specified, selecting + * will not start until after mouse is dragged beyond distance. Default : 1 + * + */ + private int distance; + + public MouseOptions() { + initDefault(); + } + + public String[] getCancel() { + return cancel; + } + + public int getDelay() { + return delay; + } + + public int getDistance() { + return distance; + } + + /** + * Prevents starting of the plugin on specified elements + * @param cancel array of css selectors + */ + public void setCancel(String... cancel) { + this.cancel = cancel; + } + + /** + * Time in milliseconds to define when the plugin should start. It helps + * preventing unwanted selections when clicking on an element. + * @param delay + */ + public void setDelay(int delay) { + this.delay = delay; + } + + /** + * Tolerance, in pixels, for when plugin should start. If specified, selecting + * will not start until after mouse is dragged beyond distance. Default : 1 + * @param distance + */ + public void setDistance(int distance) { + this.distance = distance; + } + + protected void initDefault() { + delay = 0; + distance = 1; //by default, the mouse have to move one pixel ! + cancel = new String[]{"input", "option"}; + } +} + diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MousePlugin.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MousePlugin.java new file mode 100755 index 00000000..a5cd2b16 --- /dev/null +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MousePlugin.java @@ -0,0 +1,329 @@ +/* + * Copyright 2010 The gwtquery plugins 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.dom.client.NodeList; +import com.google.gwt.query.client.Function; +import com.google.gwt.query.client.GQuery; +import com.google.gwt.query.client.JSArray; +import com.google.gwt.query.client.plugins.Events; + +/** + * Base class for all plug-in that need to handle some mouse interactions. + * + * @author Julien Dramaix (julien.dramaix@gmail.com, @jdramaix) + * + */ +public abstract class MousePlugin extends GQueryUi { + + private Event mouseDownEvent; + private boolean mouseStarted = false; + private Duration mouseUpDuration; + // private int mouseDownX; + // private int mouseDownY; + private MouseOptions options; + private boolean preventClickEvent = false; + + public MousePlugin(Element element) { + super(element); + } + + public MousePlugin(GQuery gq) { + super(gq); + } + + public MousePlugin(JSArray elements) { + super(elements); + } + + public MousePlugin(NodeList list) { + super(list); + } + + protected void destroyMouseHandler() { + as(Events.Events) + .unbind(Event.ONMOUSEDOWN | Event.ONCLICK, getPluginName()); + } + + /** + * Return a String identifying the plugin. This string is used as namespace + * when we bind handlers. + * + * @return + */ + protected abstract String getPluginName(); + + /** + * This method initialize all needed handlers + * + * @param options + */ + protected void initMouseHandler(MouseOptions options) { + this.options = options; + + for (final Element e : elements()) { + + $(e).as(Events.Events).bind(Event.ONMOUSEDOWN, getPluginName(), + (Object) null, new Function() { + @Override + public boolean f(com.google.gwt.user.client.Event event) { + return mouseDown(e, Event.create(event)); + + } + }).bind(Event.ONCLICK, getPluginName(), (Object) null, + new Function() { + @Override + public boolean f(com.google.gwt.user.client.Event event) { + preventClickEvent |= !mouseClick(e, Event.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, Event event) { + return true; + } + + /** + * Method called when mouse click + * + * @param element + * @param event + * @return + */ + protected boolean mouseClick(Element element, Event event) { + return true; + } + + /** + * Method called when mouse down occur on the element. + * + * You should not override this method. Instead, override + * {@link #mouseStart(Element, Event)} method + * + * @param element + * @param event + * @return + */ + protected boolean mouseDown(Element element, Event event) { + + // test if an other plugin handle the mouseStart + if (isEventAlreadyHandled(event)) { + return false; + } + if (mouseStarted) { // 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)) { + mouseStarted = mouseStart(element, event); + if (!mouseStarted) { + event.getOriginalEvent().preventDefault(); + return true; + } + } + + bindOtherMouseEvent(element); + + event.getOriginalEvent().preventDefault(); + + markEventAsHandled(event); + + return true; + } + + /** + * Method called when the mouse is dragging + * + * @param element + * @param event + * @return + */ + protected abstract boolean mouseDrag(Element element, Event event); + + /** + * Method called on MouseMove event. + * + * You should not override this method. Instead, override + * {@link #mouseMove(Element, Event)} method + * + * @param element + * @param event + * @return + */ + protected boolean mouseMove(Element element, Event event) { + if (mouseStarted) { + event.getOriginalEvent().preventDefault(); + return mouseDrag(element, event); + } + + if (delayConditionMet() && distanceConditionMet(event)) { + mouseStarted = mouseStart(element, mouseDownEvent); + if (mouseStarted) { + mouseDrag(element, event); + } else { + mouseUp(element, event); + } + } + + return !mouseStarted; + } + + /** + * Method called when the mouse is clicked and all conditions for starting the + * plugin are met. + * + * @param element + * @param event + * @return + */ + protected abstract boolean mouseStart(Element element, Event event); + + /** + * Method called when the mouse button is released + * + * @param element + * @param event + * @return + */ + protected abstract boolean mouseStop(Element element, Event event); + + /** + * Method called when mouse is released.. + * + * You should not override this method. Instead, override + * {@link #mouseStop(Element, Event)} method + * + * @param element + * @param event + * @return + */ + protected boolean mouseUp(Element element, Event event) { + unbindOtherMouseEvent(); + if (mouseStarted) { + mouseStarted = false; + preventClickEvent = (event.getCurrentEventTarget() == mouseDownEvent + .getCurrentEventTarget()); + mouseStop(element, event); + } + return false; + + } + + private void bindOtherMouseEvent(final Element element) { + + $(document).as(Events.Events).bind(Event.ONMOUSEMOVE, getPluginName(), + (Object) null, new Function() { + @Override + public boolean f(com.google.gwt.user.client.Event e) { + mouseMove(element, Event.create(e)); + return false; + } + }).bind(Event.ONMOUSEUP, getPluginName(), (Object) null, + new Function() { + @Override + public boolean f(com.google.gwt.user.client.Event e) { + mouseUp(element, Event.create(e)); + return false; + } + }); + } + + private boolean delayConditionMet() { + + if (mouseUpDuration == null) { + return false; + } + + return options.getDelay() <= mouseUpDuration.elapsedMillis(); + } + + private boolean distanceConditionMet(Event event) { + int neededDistance = options.getDistance(); + int mouseDownX = mouseDownEvent.getClientX(); + int mouseDownY = mouseDownEvent.getClientY(); + int xMouseDistance = Math.abs(mouseDownX - event.getClientX()); + int yMouseDistance = Math.abs(mouseDownY - event.getClientY()); + // 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(xMouseDistance * xMouseDistance + + yMouseDistance * yMouseDistance); + return mouseDistance >= neededDistance; + } + + private native boolean isEventAlreadyHandled(Event event)/*-{ + var result = event.mouseHandled ? event.mouseHandled : false; + return result; + }-*/; + + private native void markEventAsHandled(Event event)/*-{ + event.mouseHandled = true; + }-*/; + + private boolean notHandleMouseDown(Element element, Event 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(Event mouseDownEvent) { + this.mouseDownEvent = mouseDownEvent; + this.mouseUpDuration = new Duration(); + } + + private void unbindOtherMouseEvent() { + $(document).as(Events.Events).unbind((Event.ONMOUSEUP | Event.ONMOUSEMOVE), + getPluginName()); + } + +}