diff options
author | Sven Gothel <sgothel@jausoft.com> | 2013-10-15 15:36:03 +0200 |
---|---|---|
committer | Sven Gothel <sgothel@jausoft.com> | 2013-10-15 15:36:03 +0200 |
commit | bc72e232a4b74c2be8c91c540a7b6153bfefb8c0 (patch) | |
tree | a96ef243eb96fad1ca9d304eae6b3479c8d1facd /src/newt/classes/com/jogamp | |
parent | 5ac6d508c7208ac4fe5d057a9ea1bdcba5f7b998 (diff) |
Bug 861 - NEWT: Unify MouseEvent Processing incl. gesture processing
We processed MouseEvents within NEWT as follows:
sendMouseEvent/enqueueMouseEvent -> doMouseEvent,
- called by native code to be delivered via consumeMouseEvent (now or later)
- events are validated (move/drag, boundaries)
- missing events are synthesized (click, enter, ..)
as well as in several factories, i.e.:
- AWTNewtEventFactory (1:1)
- AndroidNewtEventFactory
- synthesized events .. (click, ..)
- android typed gesture detection (drag -> 1 finger scroll..)
The latter enqueues events do Window/Display directly to be consumed by WindowImpl.
Then users may have their own gesture detection etc.
+++
This change unifies mouse/pointer event processing within NEWT within consumeEvent(..)
which represents a common entry point.
Gesture processing is now realized w/ a public API
- GestureHandler
- GestureHandler.GestureListener
- GestureHandler.GesureEvent
which supplies:
- default impl. of optional gesture handlers (scroll, .. - default: enabled)
- public API to add/remove gesture-handler and -listener
+++
This allows our impl. to scale better in support of
more multiple pointer devices (-> Win7/Win8, X11, ..).
Diffstat (limited to 'src/newt/classes/com/jogamp')
8 files changed, 997 insertions, 52 deletions
diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index 8a43ef153..02727353f 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -30,12 +30,14 @@ package com.jogamp.newt; import java.util.List; +import com.jogamp.newt.event.GestureHandler; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowListener; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.MouseListener; + import jogamp.newt.Debug; import jogamp.newt.WindowImpl; @@ -553,15 +555,12 @@ public interface Window extends NativeWindow, WindowClosingProtocol { // /** - * - * Appends the given {@link com.jogamp.newt.event.MouseListener} to the end of - * the list. + * Appends the given {@link MouseListener} to the end of the list. */ void addMouseListener(MouseListener l); /** - * - * Inserts the given {@link com.jogamp.newt.event.MouseListener} at the + * Inserts the given {@link MouseListener} at the * specified position in the list.<br> * * @param index Position where the listener will be inserted. @@ -572,10 +571,61 @@ public interface Window extends NativeWindow, WindowClosingProtocol { */ void addMouseListener(int index, MouseListener l); + /** + * Removes the given {@link MouseListener} from the list. + */ void removeMouseListener(MouseListener l); + /** + * Returns the {@link MouseListener} from the list at the given index. + */ MouseListener getMouseListener(int index); + /** + * Returns all {@link MouseListener} + */ MouseListener[] getMouseListeners(); - + + /** Enable or disable default {@link GestureHandler}. Default is enabled. */ + void setDefaultGesturesEnabled(boolean enable); + /** Return true if default {@link GestureHandler} are enabled. */ + boolean areDefaultGesturesEnabled(); + /** + * Appends the given {@link GestureHandler} to the end of the list. + */ + void addGestureHandler(GestureHandler gh); + /** + * Inserts the given {@link GestureHandler} at the + * specified position in the list.<br> + * + * @param index Position where the listener will be inserted. + * Should be within (0 <= index && index <= size()). + * An index value of -1 is interpreted as the end of the list, size(). + * @param l The listener object to be inserted + * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index <= size()), or -1 + */ + void addGestureHandler(int index, GestureHandler gh); + /** + * Removes the given {@link GestureHandler} from the list. + */ + void removeGestureHandler(GestureHandler gh); + /** + * Appends the given {@link GestureHandler.GestureListener} to the end of the list. + */ + void addGestureListener(GestureHandler.GestureListener gl); + /** + * Inserts the given {@link GestureHandler.GestureListener} at the + * specified position in the list.<br> + * + * @param index Position where the listener will be inserted. + * Should be within (0 <= index && index <= size()). + * An index value of -1 is interpreted as the end of the list, size(). + * @param l The listener object to be inserted + * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index <= size()), or -1 + */ + void addGestureListener(int index, GestureHandler.GestureListener gl); + /** + * Removes the given {@link GestureHandler.GestureListener} from the list. + */ + void removeGestureListener(GestureHandler.GestureListener gl); } diff --git a/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java b/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java new file mode 100644 index 000000000..bc67cbee6 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/DoubleTapScrollGesture.java @@ -0,0 +1,346 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.event; + +import jogamp.newt.Debug; + +/** + * 2 pointer scroll/rotate gesture handler processing {@link MouseEvent}s + * while producing {@link MouseEvent#EVENT_MOUSE_WHEEL_MOVED} events if gesture is completed. + * <p> + * Criteria related to parameters: + * <pre> + * - doubleTapSlop (scaled in pixels): + * - Max 2 finger distance to start 'scroll' mode + * - Max. distance diff of current 2-pointer middle and initiated 2-pointer middle. + * + * - touchSlop (scaled in pixels): + * - Min. movement w/ 2 pointer within ScaledDoubleTapSlop starting 'scroll' mode + * + * - Avoid computation if not within gesture, especially for MOVE/DRAG + * + * - Only allow gesture to start with PRESS + * + * - Leave gesture completely with RELEASE of both/all fingers, or dist-diff exceeds doubleTapSlop + * + * - Tolerate temporary lift 1 of 2 pointer + * + * - Always validate pointer-id + * </pre> + * </p> + * Implementation uses a n-state to get detect gesture: + * <p> + * <table border="1"> + * <tr><th>from</th> <th>to</th> <th>action</th></tr> + * <tr><td>NONE</td> <td>1PRESS</td> <td>1-pointer-pressed</td></tr> + * <tr><td>1PRESS</td> <td>2PRESS_T</td> <td>2-pointer-pressed within doubleTapSlope</td></tr> + * <tr><td>2PRESS_T</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop and scrollLen >= scrollSlop</td></tr> + * <tr><td>2PRESS_C</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop</td></tr> + * <tr><td>SCROLL</td> <td>SCROLL</td> <td>2-pointer dragged, dist-diff within doubleTapSlop</td></tr> + * </table> + * State ST_2PRESS_C merely exist to pick up gesture after one pointer has been lost temporarily. + * </p> + * <p> + * {@link #isWithinGesture()} returns gestureState >= 2PRESS_C + * </p> + */ +public class DoubleTapScrollGesture implements GestureHandler { + /** Scroll threshold in pixels (fallback), defaults to 16 pixels. Can be overriden by integer property <code>newt.event.scroll_slop_pixel</code>.*/ + public static final int SCROLL_SLOP_PIXEL; + /** Two pointer 'double tap' slop in pixels (fallback), defaults to 104 pixels. Can be overriden by integer property <code>newt.event.double_tap_slop_pixel</code>.*/ + public static final int DOUBLE_TAP_SLOP_PIXEL; + + /** Scroll threshold in millimeter, defaults to 3 mm. Can be overriden by integer property <code>newt.event.scroll_slop_mm</code>.*/ + public static final float SCROLL_SLOP_MM; + /** Two pointer 'double tap' slop in millimeter, defaults to 20 mm. Can be overriden by integer property <code>newt.event.double_tap_slop_mm</code>.*/ + public static final float DOUBLE_TAP_SLOP_MM; + + static { + Debug.initSingleton(); + + SCROLL_SLOP_PIXEL = Debug.getIntProperty("newt.event.scroll_slop_pixel", true, 16); + DOUBLE_TAP_SLOP_PIXEL = Debug.getIntProperty("newt.event.double_tap_slop_pixel", true, 104); + SCROLL_SLOP_MM = Debug.getIntProperty("newt.event.scroll_slop_mm", true, 3); + DOUBLE_TAP_SLOP_MM = Debug.getIntProperty("newt.event.double_tap_slop_mm", true, 20); + } + + private static final int ST_NONE = 0; + private static final int ST_1PRESS = 1; + private static final int ST_2PRESS_T = 2; + private static final int ST_2PRESS_C = 3; + private static final int ST_SCROLL = 4; + + private final int scrollSlop, scrollSlopSquare, doubleTapSlop, doubleTapSlopSquare; + private final float[] scrollDistance = new float[] { 0f, 0f }; + private int[] pIds = new int[] { -1, -1 }; + /** See class docu */ + private int gestureState; + private int sqStartDist; + private int lastX, lastY; + private int pointerDownCount; + private MouseEvent hitGestureEvent; + + private static final int getSquareDistance(float x1, float y1, float x2, float y2) { + final int deltaX = (int) x1 - (int) x2; + final int deltaY = (int) y1 - (int) y2; + return deltaX * deltaX + deltaY * deltaY; + } + + private int gesturePointers(final MouseEvent e, final int excludeIndex) { + int j = 0; + for(int i=e.getPointerCount()-1; i>=0; i--) { + if( excludeIndex != i ) { + final int id = e.getPointerId(i); + if( pIds[0] == id || pIds[1] == id ) { + j++; + } + } + } + return j; + } + + /** + * scaledScrollSlop < scaledDoubleTapSlop + * @param scaledScrollSlop Distance a pointer can wander before we think the user is scrolling in <i>pixels</i>. + * @param scaledDoubleTapSlop Distance in <i>pixels</i> between the first touch and second touch to still be considered a double tap. + */ + public DoubleTapScrollGesture(int scaledScrollSlop, int scaledDoubleTapSlop) { + scrollSlop = scaledScrollSlop; + scrollSlopSquare = scaledScrollSlop * scaledScrollSlop; + doubleTapSlop = scaledDoubleTapSlop; + doubleTapSlopSquare = scaledDoubleTapSlop * scaledDoubleTapSlop; + pointerDownCount = 0; + clear(true); + if(DEBUG) { + System.err.println("DoubleTapScroll scrollSlop (scaled) "+scrollSlop); + System.err.println("DoubleTapScroll doubleTapSlop (scaled) "+doubleTapSlop); + } + } + + public String toString() { + return "DoubleTapScroll[state "+gestureState+", in "+isWithinGesture()+", has "+(null!=hitGestureEvent)+", pc "+pointerDownCount+"]"; + } + + @Override + public void clear(boolean clearStarted) { + scrollDistance[0] = 0f; + scrollDistance[1] = 0f; + hitGestureEvent = null; + if( clearStarted ) { + gestureState = ST_NONE; + sqStartDist = 0; + pIds[0] = -1; + pIds[1] = -1; + lastX = 0; + lastY = 0; + } + } + + @Override + public boolean isWithinGesture() { + return ST_2PRESS_C <= gestureState; + } + + @Override + public boolean hasGesture() { + return null != hitGestureEvent; + } + + @Override + public InputEvent getGestureEvent() { + if( null != hitGestureEvent ) { + final MouseEvent ge = hitGestureEvent; + int modifiers = ge.getModifiers(); + final float[] rotationXYZ = ge.getRotation(); + rotationXYZ[0] = scrollDistance[0] / scrollSlop; + rotationXYZ[1] = scrollDistance[1] / scrollSlop; + if( rotationXYZ[0]*rotationXYZ[0] > rotationXYZ[1]*rotationXYZ[1] ) { + // Horizontal scroll -> SHIFT + modifiers |= com.jogamp.newt.event.InputEvent.SHIFT_MASK; + } + return new MouseEvent(MouseEvent.EVENT_MOUSE_WHEEL_MOVED, ge.getSource(), ge.getWhen(), modifiers, + ge.getAllPointerTypes(), ge.getAllPointerIDs(), + ge.getAllX(), ge.getAllY(), ge.getAllPressures(), ge.getMaxPressure(), + ge.getButton(), ge.getClickCount(), rotationXYZ, scrollSlop); + } + return null; + } + + public final float[] getScrollDistanceXY() { + return scrollDistance; + } + + @Override + public boolean process(final InputEvent in) { + if( null != hitGestureEvent || !(in instanceof MouseEvent) ) { + return true; + } + final MouseEvent pe = (MouseEvent)in; + if( pe.getPointerType(0).getPointerClass() != MouseEvent.PointerClass.Onscreen ) { + return false; + } + pointerDownCount = pe.getPointerCount(); + final int eventType = pe.getEventType(); + final int x0 = pe.getX(0); + final int y0 = pe.getY(0); + switch ( eventType ) { + case MouseEvent.EVENT_MOUSE_PRESSED: { + int gPtr = 0; + if( ST_NONE == gestureState && 1 == pointerDownCount ) { + pIds[0] = pe.getPointerId(0); + pIds[1] = -1; + gestureState = ST_1PRESS; + } else if( ST_NONE < gestureState && 2 == pointerDownCount && 1 == gesturePointers(pe, 0) /* w/o pressed pointer */ ) { + final int x1 = pe.getX(1); + final int y1 = pe.getY(1); + final int xm = (x0+x1)/2; + final int ym = (y0+y1)/2; + + if( ST_1PRESS == gestureState ) { + final int sqDist = getSquareDistance(x0, y0, x1, y1); + final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare; + if( isDistWithinDoubleTapSlop ) { + // very first 2-finger touch-down + gPtr = 2; + pIds[0] = pe.getPointerId(0); + pIds[1] = pe.getPointerId(1); + lastX = xm; + lastY = ym; + sqStartDist = sqDist; + gestureState = ST_2PRESS_T; + } + if(DEBUG) { + final int dist = (int)Math.round(Math.sqrt(sqDist)); + System.err.println(this+".pressed.1: dist "+dist+", gPtr "+gPtr+", distWithin2DTSlop "+isDistWithinDoubleTapSlop+", last "+lastX+"/"+lastY+", "+pe); + } + } else if( ST_2PRESS_C == gestureState ) { // pick up gesture after temp loosing one pointer + gPtr = gesturePointers(pe, -1); + if( 2 == gPtr ) { + // same pointers re-touch-down + lastX = xm; + lastY = ym; + } else { + // other 2 pointers .. should rarely happen! + clear(true); + } + } + } + if(DEBUG) { + System.err.println(this+".pressed: gPtr "+gPtr+", this "+lastX+"/"+lastY+", "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_RELEASED: { + pointerDownCount--; // lifted + final int gPtr = gesturePointers(pe, 0); // w/o lifted pointer + if ( 1 == gPtr ) { + // tolerate lifting 1 of 2 gesture pointers temporary + gestureState = ST_2PRESS_C; + } else if( 0 == gPtr ) { + // all lifted + clear(true); + } + if(DEBUG) { + System.err.println(this+".released: gPtr "+gPtr+", "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_DRAGGED: { + if( 2 == pointerDownCount && ST_1PRESS < gestureState ) { + final int gPtr = gesturePointers(pe, -1); + if( 2 == gPtr ) { + // same pointers + final int x1 = pe.getX(1); + final int y1 = pe.getY(1); + final int xm = (x0+x1)/2; + final int ym = (y0+y1)/2; + final int sqDist = getSquareDistance(x0, y0, x1, y1); + final boolean isDistDiffWithinDoubleTapSlop = Math.abs(sqDist - sqStartDist) <= doubleTapSlopSquare; + if( isDistDiffWithinDoubleTapSlop ) { + switch( gestureState ) { + case ST_2PRESS_T: { + final int sqScrollLen = getSquareDistance(lastX, lastY, xm, ym); + if( sqScrollLen > scrollSlopSquare ) { // min. scrolling threshold reached + gestureState = ST_SCROLL; + } + } break; + + case ST_2PRESS_C: + gestureState = ST_SCROLL; + break; + + case ST_SCROLL: + scrollDistance[0] = lastX - xm; + scrollDistance[1] = lastY - ym; + hitGestureEvent = pe; + break; + } + if(DEBUG) { + final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare; + final int dist = (int)Math.round(Math.sqrt(sqDist)); + final int sqScrollLen = getSquareDistance(lastX, lastY, xm, ym); + final int scrollLen = (int)Math.round(Math.sqrt(sqScrollLen)); + System.err.println(this+".dragged.1: pDist "+dist+", scrollLen "+scrollLen+", gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+ + ", diffDistWithinTapSlop "+isDistDiffWithinDoubleTapSlop+ + ", distWithin2DTSlop "+isDistWithinDoubleTapSlop+ + ", this "+xm+"/"+ym+", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]); + } + } else { + // distance too big .. + if(DEBUG) { + final boolean isDistWithinDoubleTapSlop = sqDist < doubleTapSlopSquare; + final int dist = (int)Math.round(Math.sqrt(sqDist)); + final int startDist = (int)Math.round(Math.sqrt(sqStartDist)); + System.err.println(this+".dragged.X1: pDist "+dist+", distStart "+startDist+", gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+ + ", diffDistWithinTapSlop "+isDistDiffWithinDoubleTapSlop+ + ", distWithin2DTSlop "+isDistWithinDoubleTapSlop+ + ", this "+xm+"/"+ym+", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]); + } + clear(true); + } + if( ST_2PRESS_T < gestureState ) { + // state ST_2PRESS_T waits for min scroll threshold ! + lastX = xm; + lastY = ym; + } + } else { + // other 2 pointers .. should rarely happen! + if(DEBUG) { + System.err.println(this+".dragged.X2: gPtr "+gPtr+" ["+pIds[0]+", "+pIds[1]+"]"+ + ", last "+lastX+"/"+lastY+", d "+scrollDistance[0]+"/"+scrollDistance[1]); + } + clear(true); + } + } + } break; + + default: + } + return null != hitGestureEvent; + } +} diff --git a/src/newt/classes/com/jogamp/newt/event/GestureHandler.java b/src/newt/classes/com/jogamp/newt/event/GestureHandler.java new file mode 100644 index 000000000..2c8f29bb7 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/GestureHandler.java @@ -0,0 +1,143 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.event; + +import jogamp.newt.Debug; + +/** + * Generic gesture handler interface designed to allow pass-through + * filtering of {@link InputEvent}s. + * <p> + * To avoid negative impact on event processing, + * implementation shall restrict computation as much as possible + * and only within it's appropriate gesture states. + * </p> + * <p> + * To allow custom user events, other than the <i>normal</i> {@link InputEvent}s, + * a user may return a {@link GestureEvent} in it's implementation. + * </p> + */ +public interface GestureHandler { + public static final boolean DEBUG = Debug.debug("Window.MouseEvent"); + + /** A custom gesture event */ + @SuppressWarnings("serial") + public static class GestureEvent extends InputEvent { + /** A gesture has been detected. */ + public static final short EVENT_GESTURE_DETECTED = 400; + + private final GestureHandler handler; + + /** + * Creates a gesture event with default type {@link #EVENT_GESTURE_DETECTED}. + * + * @param source + * @param when + * @param modifiers + * @param handler + */ + public GestureEvent(Object source, long when, int modifiers, GestureHandler handler) { + super(EVENT_GESTURE_DETECTED, source, when, modifiers); + this.handler = handler; + } + + /** + * Creates a gesture event with custom <i>event_type</i> ! + * @param event_type must lie within [400..599] + * @param source + * @param when + * @param modifiers + * @param handler + */ + public GestureEvent(short event_type, Object source, long when, int modifiers, GestureHandler handler) { + super(event_type, source, when, modifiers); + this.handler = handler; + } + + /** Return the {@link GestureHandler}, which produced the event. */ + public final GestureHandler getHandler() { return handler; } + } + + /** + * Listener for {@link GestureEvent}s. + * + * @see GestureEvent + */ + public static interface GestureListener extends NEWTEventListener + { + /** {@link GestureHandler} {@link GestureHandler#hasGesture() has detected} the gesture. */ + public void gestureDetected(GestureEvent gh); + } + + /** + * Clears state of handler, i.e. resets all states incl. previous detected gesture. + * @param clearStarted if true, also clears {@link #isWithinGesture() started} state, + * otherwise stay within gesture - if appropriate. + * Staying within a gesture allows fluent continuous gesture sequence, + * e.g. for scrolling. + */ + public void clear(boolean clearStarted); + + /** + * Returns true if a previous {@link #process(InputEvent)} command produced a gesture, + * which has not been {@link #clear(boolean) cleared}. + * Otherwise returns false. + */ + public boolean hasGesture(); + + /** + * Returns the corresponding {@link InputEvent} for the gesture as detected by + * a previous {@link #process(InputEvent)}, which has not been {@link #clear(boolean) cleared}. + * Otherwise returns null. + * <p> + * Only implemented for gestures mapping to {@link InputEvent}s. + * </p> + */ + public InputEvent getGestureEvent(); + + /** + * Returns true if within a gesture as detected by a previous {@link #process(InputEvent)} command, + * which has not been {@link #clear(boolean) cleared}. + * Otherwise returns false. + */ + public boolean isWithinGesture(); + + /** + * Process the given {@link InputEvent} and returns true if it produced the gesture. + * Otherwise returns false. + * <p> + * If a gesture was already detected previously and has not been cleared, + * method does not process the event and returns true. + * </p> + * <p> + * Besides validation of the event's details, + * the handler may also validate the {@link InputEvent.InputClass} and/or {@link InputEvent.InputType}. + * </p> + */ + public boolean process(InputEvent e); +} diff --git a/src/newt/classes/com/jogamp/newt/event/InputEvent.java b/src/newt/classes/com/jogamp/newt/event/InputEvent.java index b04ebc1af..7712b77e7 100644 --- a/src/newt/classes/com/jogamp/newt/event/InputEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/InputEvent.java @@ -39,6 +39,14 @@ import com.jogamp.newt.Window; @SuppressWarnings("serial") public abstract class InputEvent extends NEWTEvent { + /** Interface marking class of input types */ + public static interface InputClass { + } + + /** Interface marking type of input devices */ + public static interface InputType { + } + public static final int SHIFT_MASK = 1 << 0; public static final int CTRL_MASK = 1 << 1; public static final int META_MASK = 1 << 2; diff --git a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java index 8533a37c6..2c137ab77 100644 --- a/src/newt/classes/com/jogamp/newt/event/MouseEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/MouseEvent.java @@ -40,8 +40,8 @@ package com.jogamp.newt.event; * http://www.w3.org/Submission/pointer-events/#pointerevent-interface * </p> * <p> - * In case an instance represents multi-touch events, i.e. {@link #getPointerCount()} is > 1, - * the first data element represents the pointer which triggered the action if individual to one pointer.<br/> + * In case an instance represents a multiple-pointer event, i.e. {@link #getPointerCount()} is > 1, + * the first data element of the multiple-pointer fields represents the pointer which triggered the action.<br/> * For example {@link #getX(int) e.getX(0)} at {@link #EVENT_MOUSE_PRESSED} returns the data of the pressed pointer, etc. * </p> */ @@ -49,12 +49,12 @@ package com.jogamp.newt.event; public class MouseEvent extends InputEvent { /** Class of pointer types */ - public static enum PointerClass{ + public static enum PointerClass implements InputEvent.InputClass { Offscreen, Onscreen, Undefined; } /** Type of pointer devices */ - public static enum PointerType{ + public static enum PointerType implements InputEvent.InputType { /** {@link PointerClass#Offscreen} mouse. */ Mouse(PointerClass.Offscreen), /** {@link PointerClass#Offscreen} touch pad, usually using fingers. */ @@ -110,7 +110,7 @@ public class MouseEvent extends InputEvent return 300; } - /** Constructor for tradition 1-pointer mouse events. */ + /** Constructor for traditional one-pointer event. */ public MouseEvent(short eventType, Object source, long when, int modifiers, int x, int y, short clickCount, short button, float[] rotationXYZ, float rotationScale) @@ -120,25 +120,48 @@ public class MouseEvent extends InputEvent this.y = new int[]{y}; this.pressure = constMousePressure; this.maxPressure= 1.0f; - this.pointerIDs = constMousePointerIDs; + this.pointerID = constMousePointerIDs; this.clickCount=clickCount; this.button=button; this.rotationXYZ = rotationXYZ; this.rotationScale = rotationScale; - this.pointerTypes = constMousePointerTypes; + this.pointerType = constMousePointerTypes; } - /** Constructor for multi-touch pointer events. */ - public MouseEvent(short eventType, Object source, long when, - int modifiers, int[] x, int[] y, float[] pressure, float maxPressure, PointerType pointerTypes[], short[] pointerids, short clickCount, - short button, float[] rotationXYZ, float rotationScale) + /** + * Constructor for a multiple-pointer event. + * <p> + * First element of multiple-pointer arrays represents the pointer which triggered the event! + * </p> + * + * @param eventType + * @param source + * @param when + * @param modifiers + * @param pointerType PointerType for each pointer (multiple pointer) + * @param pointerID Pointer ID for each pointer (multiple pointer) + * @param x X-axis for each pointer (multiple pointer) + * @param y Y-axis for each pointer (multiple pointer) + * @param pressure Pressure for each pointer (multiple pointer) + * @param maxPressure Maximum pointer pressure for all pointer + * @param button Corresponding mouse-button + * @param clickCount Mouse-button click-count + * @param rotationXYZ Rotation of all axis + * @param rotationScale Rotation scale + */ + public MouseEvent(short eventType, Object source, long when, int modifiers, + PointerType pointerType[], short[] pointerID, + int[] x, int[] y, float[] pressure, float maxPressure, + short button, short clickCount, float[] rotationXYZ, float rotationScale) { super(eventType, source, when, modifiers); this.x = x; this.y = y; - if(pointerids.length != pressure.length || - pointerids.length != x.length || - pointerids.length != y.length) { + final int pointerCount = pointerType.length; + if(pointerCount != pointerID.length || + pointerCount != x.length || + pointerCount != y.length || + pointerCount != pressure.length) { throw new IllegalArgumentException("All multiple pointer arrays must be of same size"); } if( 0.0f >= maxPressure ) { @@ -146,55 +169,148 @@ public class MouseEvent extends InputEvent } this.pressure = pressure; this.maxPressure= maxPressure; - this.pointerIDs = pointerids; + this.pointerID = pointerID; this.clickCount=clickCount; this.button=button; this.rotationXYZ = rotationXYZ; this.rotationScale = rotationScale; - this.pointerTypes = pointerTypes; + this.pointerType = pointerType; + } + + public MouseEvent createVariant(short newEventType) { + return new MouseEvent(newEventType, source, getWhen(), getModifiers(), pointerType, pointerID, + x, y, pressure, maxPressure, button, clickCount, rotationXYZ, rotationScale); + } + + /** + * Factory for a multiple-pointer event. + * <p> + * The index for the element of multiple-pointer arrays represents the pointer which triggered the event + * is passed via <i>actionIdx</i>. + * </p> + * + * @param eventType + * @param source + * @param when + * @param modifiers + * @param actionIdx index of multiple-pointer arrays representing the pointer which triggered the event + * @param pointerType PointerType for each pointer (multiple pointer) + * @param pointerID Pointer ID for each pointer (multiple pointer) + * @param x X-axis for each pointer (multiple pointer) + * @param y Y-axis for each pointer (multiple pointer) + * @param pressure Pressure for each pointer (multiple pointer) + * @param maxPressure Maximum pointer pressure for all pointer + * @param button Corresponding mouse-button + * @param clickCount Mouse-button click-count + * @param rotationXYZ Rotation of all axis + * @param rotationScale Rotation scale + */ + public static MouseEvent create(short eventType, Object source, long when, int modifiers, + int actionIdx, PointerType pointerType[], short[] pointerID, + int[] x, int[] y, float[] pressure, float maxPressure, + short button, short clickCount, float[] rotationXYZ, float rotationScale) { + if( 0 <= actionIdx && actionIdx < pointerType.length) { + if( 0 < actionIdx ) { + { + final PointerType aType = pointerType[actionIdx]; + pointerType[actionIdx] = pointerType[0]; + pointerType[0] = aType; + } + { + final short s = pointerID[actionIdx]; + pointerID[actionIdx] = pointerID[0]; + pointerID[0] = s; + } + { + int s = x[actionIdx]; + x[actionIdx] = x[0]; + x[0] = s; + s = y[actionIdx]; + y[actionIdx] = y[0]; + y[0] = s; + } + { + final float aPress = pressure[actionIdx]; + pressure[actionIdx] = pressure[0]; + pressure[0] = aPress; + } + } + return new MouseEvent(eventType, source, when, modifiers, + pointerType, pointerID, x, y, pressure, maxPressure, + button, clickCount, rotationXYZ, rotationScale); + } + throw new IllegalArgumentException("actionIdx out of bounds [0.."+(pointerType.length-1)+"]"); } /** * @return the count of pointers involved in this event */ - public int getPointerCount() { - return x.length; + public final int getPointerCount() { + return pointerType.length; } /** * @return the {@link PointerType} for the data at index. * return null if index not available. */ - public PointerType getPointerType(int index) { - if(index >= pointerIDs.length) { + public final PointerType getPointerType(int index) { + if(0 > index || index >= pointerType.length) { return null; } - return pointerTypes[index]; + return pointerType[index]; + } + + /** + * @return array of all {@link PointerType}s for all pointers + */ + public final PointerType[] getAllPointerTypes() { + return pointerType; } /** - * @return the pointer id for the data at index. + * @return the pointer id for the given index. * return -1 if index not available. */ - public short getPointerId(int index) { - if(index >= pointerIDs.length) { + public final short getPointerId(int index) { + if(0 > index || index >= pointerID.length) { return -1; } - return pointerIDs[index]; + return pointerID[index]; } - public short getButton() { + /** + * @return the pointer index for the given pointer id. + * return -1 if id not available. + */ + public final int getPointerIdx(short id) { + for(int i=pointerID.length-1; i>=0; i--) { + if( pointerID[i] == id ) { + return i; + } + } + return -1; + } + + /** + * @return array of all pointer IDs for all pointers + */ + public final short[] getAllPointerIDs() { + return pointerID; + } + + public final short getButton() { return button; } - public short getClickCount() { + public final short getClickCount() { return clickCount; } - public int getX() { + + public final int getX() { return x[0]; } - public int getY() { + public final int getY() { return y[0]; } @@ -203,7 +319,7 @@ public class MouseEvent extends InputEvent * @return X-Coord associated with the pointer-index. * @see getPointerId(index) */ - public int getX(int index) { + public final int getX(int index) { return x[index]; } @@ -212,20 +328,41 @@ public class MouseEvent extends InputEvent * @return Y-Coord associated with the pointer-index. * @see getPointerId(index) */ - public int getY(int index) { + public final int getY(int index) { return y[index]; } /** + * @return array of all X-Coords for all pointers + */ + public final int[] getAllX() { + return x; + } + + /** + * @return array of all Y-Coords for all pointers + */ + public final int[] getAllY() { + return y; + } + + /** * @param normalized if true, method returns the normalized pressure, i.e. <code>pressure / maxPressure</code> * @return The pressure associated with the pointer-index 0. * The value of zero is return if not available. * @see #getMaxPressure() */ - public float getPressure(boolean normalized){ + public final float getPressure(boolean normalized){ return normalized ? pressure[0] / maxPressure : pressure[0]; } + /** + * @return array of all raw, un-normalized pressures for all pointers + */ + public final float[] getAllPressures() { + return pressure; + } + /** * Returns the maximum pressure known for the input device generating this event. * <p> @@ -239,7 +376,7 @@ public class MouseEvent extends InputEvent * </ul> * </p> */ - public float getMaxPressure() { + public final float getMaxPressure() { return maxPressure; } @@ -250,7 +387,7 @@ public class MouseEvent extends InputEvent * The value of zero is return if not available. * @see #getMaxPressure() */ - public float getPressure(int index, boolean normalized){ + public final float getPressure(int index, boolean normalized){ return normalized ? pressure[index] / maxPressure : pressure[index]; } @@ -294,7 +431,7 @@ public class MouseEvent extends InputEvent * see {@link #getRotationScale()} for semantics. * </p> */ - public float[] getRotation() { + public final float[] getRotation() { return rotationXYZ; } @@ -311,15 +448,15 @@ public class MouseEvent extends InputEvent * Hence <code>scale * rotation</code> reproduces the screen distance in pixels the finger[s] have moved. * </p> */ - public float getRotationScale() { + public final float getRotationScale() { return rotationScale; } - public String toString() { + public final String toString() { return toString(null).toString(); } - public StringBuilder toString(StringBuilder sb) { + public final StringBuilder toString(StringBuilder sb) { if(null == sb) { sb = new StringBuilder(); } @@ -327,13 +464,13 @@ public class MouseEvent extends InputEvent .append(", ").append(x).append("/").append(y) .append(", button ").append(button).append(", count ") .append(clickCount).append(", rotation [").append(rotationXYZ[0]).append(", ").append(rotationXYZ[1]).append(", ").append(rotationXYZ[2]).append("] * ").append(rotationScale); - if(pointerIDs.length>0) { - sb.append(", pointer<").append(pointerIDs.length).append(">["); - for(int i=0; i<pointerIDs.length; i++) { + if(pointerID.length>0) { + sb.append(", pointer<").append(pointerID.length).append(">["); + for(int i=0; i<pointerID.length; i++) { if(i>0) { sb.append(", "); } - sb.append(pointerIDs[i]).append("/").append(pointerTypes[i]).append(": ") + sb.append(pointerID[i]).append("/").append(pointerType[i]).append(": ") .append(x[i]).append("/").append(y[i]).append(", ") .append("p[").append(pressure[i]).append("/").append(maxPressure).append("=").append(pressure[i]/maxPressure).append("]"); } @@ -356,15 +493,24 @@ public class MouseEvent extends InputEvent default: return "unknown (" + type + ")"; } } - private final int x[], y[]; + + /** PointerType for each pointer (multiple pointer) */ + private final PointerType pointerType[]; + /** Pointer-ID for each pointer (multiple pointer) */ + private final short pointerID[]; + /** X-axis for each pointer (multiple pointer) */ + private final int x[]; + /** Y-axis for each pointer (multiple pointer) */ + private final int y[]; + /** Pressure for each pointer (multiple pointer) */ + private final float pressure[]; // private final short tiltX[], tiltY[]; // TODO: A generic way for pointer axis information, see Android MotionEvent! private final short clickCount, button; + /** Rotation around the X, Y and X axis */ private final float[] rotationXYZ; + /** Rotation scale */ private final float rotationScale; - private final float pressure[]; private final float maxPressure; - private final short pointerIDs[]; - private final PointerType pointerTypes[]; private static final float[] constMousePressure = new float[]{0f}; private static final short[] constMousePointerIDs = new short[]{0}; diff --git a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java index c1bc791d8..02bb4f929 100644 --- a/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/NEWTEvent.java @@ -44,6 +44,7 @@ package com.jogamp.newt.event; * <li> WindowEvent <code>100..10x</code></li> * <li> MouseEvent <code>200..20x</code></li> * <li> KeyEvent <code>300..30x</code></li> + * <li> GestureEvent <code>400..5xx</code></li> * <li> MonitorEvent <code>600..60x</code></li> * </ul><br> */ diff --git a/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java b/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java new file mode 100644 index 000000000..3a34c6253 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/event/PinchToZoomGesture.java @@ -0,0 +1,217 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.newt.event; + +import javax.media.nativewindow.NativeSurface; + +import jogamp.newt.Debug; + +/** + * 2 pointer zoom, a.k.a. <i>pinch to zoom</i>, gesture handler processing {@link MouseEvent}s + * while producing {@link ZoomEvent}s if gesture is completed. + * <p> + * Zoom value lies within [0..2], with 1 as <i>1:1</i>. + * </p> + * <pre> + * - choosing the smallest surface edge (width/height -> x/y) + * - tolerating other fingers to be pressed and hence user to add functionality (scale, ..) + * </pre> + */ +public class PinchToZoomGesture implements GestureHandler { + public static final boolean DEBUG = Debug.debug("Window.MouseEvent"); + + /** A {@link GestureHandler.GestureEvent} denominating zoom. */ + @SuppressWarnings("serial") + public static class ZoomEvent extends GestureEvent { + private final MouseEvent pe; + private final float zoom; + public ZoomEvent(Object source, long when, int modifiers, GestureHandler handler, MouseEvent pe, float zoom) { + super(source, when, modifiers, handler); + this.pe = pe; + this.zoom = zoom; + } + /** Triggering {@link MouseEvent} */ + public final MouseEvent getTrigger() { return pe; } + /** Zoom value lies within [0..2], with 1 as <i>1:1</i>. */ + public final float getZoom() { return zoom; } + } + + private final NativeSurface surface; + private float zoom; + private int zoomLastEdgeDist; + private boolean zoomFirstTouch; + private boolean zoomMode; + private ZoomEvent zoomEvent; + private short[] pIds = new short[] { -1, -1 }; + + public PinchToZoomGesture(NativeSurface surface) { + clear(true); + this.surface = surface; + this.zoom = 1f; + } + + public String toString() { + return "PinchZoom[1stTouch "+zoomFirstTouch+", in "+isWithinGesture()+", has "+(null!=zoomEvent)+", zoom "+zoom+"]"; + } + + private int gesturePointers(final MouseEvent e, final int excludeIndex) { + int j = 0; + for(int i=e.getPointerCount()-1; i>=0; i--) { + if( excludeIndex != i ) { + final int id = e.getPointerId(i); + if( pIds[0] == id || pIds[1] == id ) { + j++; + } + } + } + return j; + } + + @Override + public void clear(boolean clearStarted) { + zoomEvent = null; + if( clearStarted ) { + zoomLastEdgeDist = 0; + zoomFirstTouch = true; + zoomMode = false; + pIds[0] = -1; + pIds[1] = -1; + } + } + + @Override + public boolean isWithinGesture() { + return zoomMode; + } + + @Override + public boolean hasGesture() { + return null != zoomEvent; + } + + @Override + public InputEvent getGestureEvent() { + return zoomEvent; + } + + /** Zoom value lies within [0..2], with 1 as <i>1:1</i>. */ + public final float getZoom() { + return zoom; + } + /** Set zoom value within [0..2], with 1 as <i>1:1</i>. */ + public final void setZoom(float zoom) { + this.zoom=zoom; + } + + @Override + public boolean process(final InputEvent in) { + if( null != zoomEvent || !(in instanceof MouseEvent) ) { + return true; + } + final MouseEvent pe = (MouseEvent)in; + if( pe.getPointerType(0).getPointerClass() != MouseEvent.PointerClass.Onscreen ) { + return false; + } + + final int pointerDownCount = pe.getPointerCount(); + final int eventType = pe.getEventType(); + final boolean useY = surface.getWidth() >= surface.getHeight(); // use smallest dimension + switch ( eventType ) { + case MouseEvent.EVENT_MOUSE_PRESSED: { + if( 1 == pointerDownCount ) { + pIds[0] = pe.getPointerId(0); + pIds[1] = -1; + } else if ( 2 <= pointerDownCount ) { // && 1 == gesturePointers(pe, 0) /* w/o pressed pointer */) { + pIds[0] = pe.getPointerId(0); + pIds[1] = pe.getPointerId(1); + } + if(DEBUG) { + System.err.println("XXX1: id0 "+pIds[0]+" -> idx0 "+0+", id1 "+pIds[1]+" -> idx1 "+1); + System.err.println(this+".pressed: down "+pointerDownCount+", gPtr "+gesturePointers(pe, -1)+", event "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_RELEASED: { + final int gPtr = gesturePointers(pe, 0); // w/o lifted pointer + if ( 1 == gPtr ) { + zoomFirstTouch = true; + zoomMode = false; + } else if( 0 == gPtr ) { + // all lifted + clear(true); + } + if(DEBUG) { + System.err.println(this+".released: down "+pointerDownCount+", gPtr "+gPtr+", event "+pe); + } + } break; + + case MouseEvent.EVENT_MOUSE_DRAGGED: { + if( 2 <= pointerDownCount ) { + final int gPtr = gesturePointers(pe, -1); + if( 2 == gPtr ) { + // same pointers + final int p0Idx = pe.getPointerIdx(pIds[0]); + final int p1Idx = pe.getPointerIdx(pIds[1]); + final int edge0 = useY ? pe.getY(p0Idx) : pe.getX(p0Idx); + final int edge1 = useY ? pe.getY(p1Idx) : pe.getX(p1Idx); + // Diff. 1:1 Zoom: finger-distance to screen-coord + if(zoomFirstTouch) { + zoomLastEdgeDist = Math.abs(edge0-edge1); + zoomFirstTouch=false; + zoomMode = true; + } else if( zoomMode ) { + final int d = Math.abs(edge0-edge1); + final int dd = d - zoomLastEdgeDist; + final float screenEdge = useY ? surface.getHeight() : surface.getWidth(); + final float incr = (float)dd / screenEdge; // [-1..1] + if(DEBUG) { + System.err.println("XXX2: id0 "+pIds[0]+" -> idx0 "+p0Idx+", id1 "+pIds[1]+" -> idx1 "+p1Idx); + System.err.println("XXX3: d "+d+", ld "+zoomLastEdgeDist+", dd "+dd+", screen "+screenEdge+" -> incr "+incr+", zoom "+zoom+" -> "+(zoom+incr)); + } + zoom += incr; + // clip value + if( 2f < zoom ) { + zoom = 2f; + } else if( 0 > zoom ) { + zoom = 0; + } + zoomLastEdgeDist = d; + zoomEvent = new ZoomEvent(pe.getSource(), pe.getWhen(), pe.getModifiers(), this, pe, zoom); + } + } + if(DEBUG) { + System.err.println(this+".dragged: down "+pointerDownCount+", gPtr "+gPtr+", event "+pe); + } + } + } break; + + default: + } + return null != zoomEvent; + } +} diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index eace0f2af..cae1a06a2 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -77,6 +77,7 @@ import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; import com.jogamp.newt.Window; +import com.jogamp.newt.event.GestureHandler; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseListener; import com.jogamp.newt.event.NEWTEvent; @@ -774,6 +775,39 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind return window.getMouseListeners(); } + @Override + public void setDefaultGesturesEnabled(boolean enable) { + window.setDefaultGesturesEnabled(enable); + } + @Override + public boolean areDefaultGesturesEnabled() { + return window.areDefaultGesturesEnabled(); + } + @Override + public final void addGestureHandler(GestureHandler gh) { + window.addGestureHandler(gh); + } + @Override + public final void addGestureHandler(int index, GestureHandler gh) { + window.addGestureHandler(index, gh); + } + @Override + public final void removeGestureHandler(GestureHandler gh) { + window.removeGestureHandler(gh); + } + @Override + public final void addGestureListener(GestureHandler.GestureListener gl) { + window.addGestureListener(-1, gl); + } + @Override + public final void addGestureListener(int index, GestureHandler.GestureListener gl) { + window.addGestureListener(index, gl); + } + @Override + public final void removeGestureListener(GestureHandler.GestureListener gl) { + window.removeGestureListener(gl); + } + //---------------------------------------------------------------------- // NativeWindow completion // |