1 /*******************************************************************************
\r
2 * Copyright 2011 See AUTHORS file.
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
15 ******************************************************************************/
\r
17 package com.badlogic.gdx.scenes.scene2d.utils;
\r
19 import com.badlogic.gdx.Gdx;
\r
20 import com.badlogic.gdx.Input.Buttons;
\r
21 import com.badlogic.gdx.scenes.scene2d.Actor;
\r
22 import com.badlogic.gdx.scenes.scene2d.InputEvent;
\r
23 import com.badlogic.gdx.scenes.scene2d.InputListener;
\r
24 import com.badlogic.gdx.utils.TimeUtils;
\r
26 /** Detects mouse over, mouse or finger touch presses, and clicks on an actor. A touch must go down over the actor and is
\r
27 * considered pressed as long as it is over the actor or within the {@link #setTapSquareSize(float) tap square}. This behavior
\r
28 * makes it easier to press buttons on a touch interface when the initial touch happens near the edge of the actor. Double clicks
\r
29 * can be detected using {@link #getTapCount()}. Any touch (not just the first) will trigger this listener. While pressed, other
\r
30 * touch downs are ignored.
\r
31 * @author Nathan Sweet */
\r
32 public class ClickListener extends InputListener {
\r
33 private float tapSquareSize = 14, touchDownX = -1, touchDownY = -1;
\r
34 private int pressedPointer = -1;
\r
35 private int pressedButton = -1;
\r
37 private boolean pressed, over, cancelled;
\r
38 private long tapCountInterval = (long)(0.4f * 1000000000l);
\r
39 private int tapCount;
\r
40 private long lastTapTime;
\r
42 public ClickListener () {
\r
45 /** @see #setButton(int) */
\r
46 public ClickListener (int button) {
\r
47 this.button = button;
\r
50 public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
\r
51 if (pressed) return false;
\r
52 if (pointer == 0 && this.button != -1 && button != this.button) return false;
\r
54 pressedPointer = pointer;
\r
55 pressedButton = button;
\r
61 public void touchDragged (InputEvent event, float x, float y, int pointer) {
\r
62 if (pointer != pressedPointer || cancelled) return;
\r
63 pressed = isOver(event.getListenerActor(), x, y);
\r
64 if (pressed && pointer == 0 && button != -1 && !Gdx.input.isButtonPressed(button)) pressed = false;
\r
66 // Once outside the tap square, don't use the tap square anymore.
\r
67 invalidateTapSquare();
\r
71 public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
\r
72 if (pointer == pressedPointer) {
\r
74 boolean touchUpOver = isOver(event.getListenerActor(), x, y);
\r
75 // Ignore touch up if the wrong mouse button.
\r
76 if (touchUpOver && pointer == 0 && this.button != -1 && button != this.button) touchUpOver = false;
\r
78 long time = TimeUtils.nanoTime();
\r
79 if (time - lastTapTime > tapCountInterval) tapCount = 0;
\r
82 clicked(event, x, y);
\r
86 pressedPointer = -1;
\r
92 public void enter (InputEvent event, float x, float y, int pointer, Actor fromActor) {
\r
93 if (pointer == -1 && !cancelled) over = true;
\r
96 public void exit (InputEvent event, float x, float y, int pointer, Actor toActor) {
\r
97 if (pointer == -1 && !cancelled) over = false;
\r
100 /** If a touch down is being monitored, the drag and touch up events are ignored until the next touch up. */
\r
101 public void cancel () {
\r
102 if (pressedPointer == -1) return;
\r
108 public void clicked (InputEvent event, float x, float y) {
\r
111 /** Returns true if the specified position is over the specified actor or within the tap square. */
\r
112 public boolean isOver (Actor actor, float x, float y) {
\r
113 Actor hit = actor.hit(x, y, true);
\r
114 if (hit == null || !hit.isDescendantOf(actor)) return inTapSquare(x, y);
\r
118 public boolean inTapSquare (float x, float y) {
\r
119 if (touchDownX == -1 && touchDownY == -1) return false;
\r
120 return Math.abs(x - touchDownX) < tapSquareSize && Math.abs(y - touchDownY) < tapSquareSize;
\r
123 /** The tap square will not longer be used for the current touch. */
\r
124 public void invalidateTapSquare () {
\r
129 /** Returns true if a touch is over the actor or within the tap square. */
\r
130 public boolean isPressed () {
\r
134 /** Returns true if the mouse or touch is over the actor or pressed and within the tap square. */
\r
135 public boolean isOver () {
\r
136 return over || pressed;
\r
139 public void setTapSquareSize (float halfTapSquareSize) {
\r
140 tapSquareSize = halfTapSquareSize;
\r
143 public float getTapSquareSize () {
\r
144 return tapSquareSize;
\r
147 /** @param tapCountInterval time in seconds that must pass for two touch down/up sequences to be detected as consecutive taps. */
\r
148 public void setTapCountInterval (float tapCountInterval) {
\r
149 this.tapCountInterval = (long)(tapCountInterval * 1000000000l);
\r
152 /** Returns the number of taps within the tap count interval for the most recent click event. */
\r
153 public int getTapCount () {
\r
157 public float getTouchDownX () {
\r
161 public float getTouchDownY () {
\r
165 /** The button that initially pressed this button or -1 if the button is not pressed. */
\r
166 public int getPressedButton () {
\r
167 return pressedButton;
\r
170 /** The pointer that initially pressed this button or -1 if the button is not pressed. */
\r
171 public int getPressedPointer () {
\r
172 return pressedPointer;
\r
175 /** @see #setButton(int) */
\r
176 public int getButton () {
\r
180 /** Sets the button to listen for, all other buttons are ignored. Default is {@link Buttons#LEFT}. Use -1 for any button. */
\r
181 public void setButton (int button) {
\r
182 this.button = button;
\r