I have the following problem in LibGDX. When you move over an Actor you get the enter event from the ClickListener and the exit event when you move out of the bounding box of the Actor. I keep a boolean mouseOver that tells me if the cursor is over an Actor with these events. But when you click on the Actor and after releasing the mouse, an exit event is given. So after releasing the mouse click, it is as if the cursor isn't over the Actor anymore, while it is.
How can I keep a correct state for the boolean mouseOver? In other words: how can I know if the mouse is over my Actor when a TouchUp event has occurred as in the above scenario.
You can override touchUp and set the boolean mouseOver to true there.
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
...
mouseOver = true;
}
The best hack I found so far is to look at the pointer arg in the exit and enter overrides and look if the mouse is clicked. I'm still looking for a cleaner solution though.
addListener(new ClickListener() {
#Override
public void enter(InputEvent event, float x, float y, int pointer,
Actor fromActor) {
mouseOver = pointer == -1 ? true : mouseOver;
super.enter(event, x, y, pointer, fromActor);
}
#Override
public void exit(InputEvent event, float x, float y, int pointer,
Actor toActor) {
mouseOver = pointer == -1 ? false : mouseOver;
super.exit(event, x, y, pointer, toActor);
}
}
Set mouseOver to false only if the Actor toActor of exit is not the actor you added the listener to:
myActor.addListener(new ClickListener() {
...
#Override
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
...
if (toActor != myActor) // and even myActor's children if it is a Group
mouseOver = false;
}
});
Related
I want to use the exit() method in the InputListener to see wheter the cursor is inside the button or not.
Here is the explanation in the libGDX docs.
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor)
Called any time the mouse cursor or a finger touch is moved out of an actor.
But when I put my cursor on the button and then move it outside the button, the method is not called. I am testing it by a System.out.println("exited"); and I get nothing in the console.
EDIT:
LibGDX Version: Latest Stable Nightlies
InputListener implementation:
//This button class is a custom class to make button creation easier. This is the constructor.
public Button(Vector2 position, String packLocation, String text, Stage stage, BitmapFont font, Color color) {
//Removed buttonStyle creation etc. to shorten the code.
button = new TextButton(text, buttonStyle);
button.setPosition(position.x, position.y);
stage.addActor(button);
Gdx.input.setInputProcessor(stage);
pressed = false;
button.addListener(new ClickListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
pressed = true;
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
pressed = false;
}
#Override
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
System.out.println("exited");
}
});
}
EDIT:
Hovering over the button also does not change the texture of the button as I set it to like so:
buttonStyle.over = skin.getDrawable("over");
But clicking does.
After searching for a couple hours I finally found what was missing. stage.act(); had to be called in the render method. This both gave functionality to the texture change when we hover over the button and also the enter/exit methods in the InputListener.
I am using libGDX on Android. I set up the Stage and added a CustomActor with a custom draw. It's working properly. However, I am not able to get any logs for the touchDragged method via the InputListener for this actor. Even the code within it does not run.
Here is the required exposure to the code:
public class CustomActor extends Actor {
public CustomActor() {
this.setListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
return true;
}
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
//This log doesn't print up!
Gdx.app.log("CustomActor","touchDragged");
//This code doesn't work either
Vector2 v = CustomActor.this.localToParentCoordinates(new Vector2(x,y));
CustomActor.this.setPosition(v.x, v.y);
}
});
}
}
Can anyone help me out here on what am I missing ?
Add the Stage you are using as InputProcessor to receive the events else the Stage can't forward the event to the actors.
Gdx.input.setInputProcessor(stage);
Should do it.
try using public boolean addListener (EventListener listener) {...} method of Actor. Which version of libgdx are you using? I think actor does not have "setListener" method.
Check your actor bounds. (width & height)
i am trying to escalate touchDown and touchUp events from an actor Object to my applicationListener class. To do so, i called fire(event); in the InputListener of my Actor
this.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("Example", "touch started at (" + x + ", " + y + ")");
fire(event);
return true;
}
public void touchUp(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("Example", "touch ended at (" + x + ", " + y + ")");
}
});
To handle the event in my ApplicationListener class (which contains the stage of the actors), i added an InputListener to the stage
Gdx.input.setInputProcessor(stage);
stage.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("FIRE!!!", "I CAUGHT A FIRED EVENT!");
event.stop();
return true;
}
public void touchUp(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("FIRE!!!", "the fired event even touchupped.");
}
});
However when i touch my actor, i get a StackOverflowError as well as a ton of exceptions from several InputListeners (i assume that the event is not stopped properly and propagated to all actors in my scene). What am i missing here?
Also after firing the event i cant cast event.getTarget() (which is an Actor) to my Actor-Subclass anymore, which works just fine if i do this in the ActorSubclass itself. Meaning the following code creates an error when used in the ApplicationListener, but works in the MyActor class:
MyActor actor = (MyActor)event.getTarget();
Since the target is in fact a MyActor Object, how can i access it not only as Actor, but as MyActor?
As #chase said you are recursivelly calling the fire(even) method, as you can only set 1 InputProcessor (your stage) and so always the same method recieves the event.
You should instead use a InputMultiplexer:
add the Stage and your ApplicationListener as InputProcessor to the InputMultiplexer
Set the InputMulitplexer as the InputProcessor by calling Gdx.input.setInputProcessor(multiplexer);.
The InputMultiplexer will then give the event to the first InputProcessor and if it returns false, it will send the event to the next one.
So in your Stage you just need to return false.
But i don't understand why you are adding your Stage as a InputProcessor if you don't want it to handle inputs...
Anyways in scene2d you don't need to add an InputProcessor to the stage as it allready has one. Also the Actor allready has a boolean touchDown(float x, float y, int pointer) and other helpful methods.
A useful link: Scene2D wiki article
Aren't you just making a recursive call at this point by refiring the event every time you handle it?
Why not just return false from your listener?
Why is my TextButton, from libgdx not responding on clicks?
I have a button, this button has a Listener, but it doesn't respond.
The button is showing, but it doesn't respond on mouse clicks.
public MyStage extends Stage {
...
next.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button)
{
Gdx.app.log(ApothecaryGame.LOG, "Pressed: Next button.");
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log( ApothecaryGame.LOG, "Released: Next button.");
super.touchUp( event, x, y, pointer, button );
nextPage();
}
} );
this.addActor(next);
}
Add a ClickListener to your button. It'll look something like this.
button.addListener(new ClickListener() {
public void clicked(InputEvent event, float x, float y) {
// Do something interesting here...
}
});
Also, make sure that you set the stage to be an input processor, otherwise it won't see the events.
Gdx.input.setInputProcessor(stage);
I've created a simple test with a touchDown() event on an image actor. The event works on the stage but not on the actor.
Here is the code:
I set the InputProcessor in the constructor of the parent class (AbstractScreen).
Gdx.input.setInputProcessor(this.stage);
In the sub class:
private TextureAtlas atlas;
private Image boardImg;
public void show(){
super.show();
atlas = new TextureAtlas(Gdx.files.internal("data/packs/mainMenu.atlas"));
AtlasRegion board = atlas.findRegion("board");
boardImg = new Image(board);
boardImg.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
Gdx.app.log("aaaa", "down");
return true;
}
});
stage.addActor(boardImg);
stage.addListener(new InputListener() {
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log("aaaa", "stage down");
return true;
}
});
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
boardImg.setPosition(stage.getWidth()/2 - boardImg.getWidth()/2, stage.getHeight()/4);
}
I'm testing both on the desktop and on the android (same results).
I get the "stage down" event but not the event of the actor. I've tried also without having the stage event.
Do all of these works:
boardImg.setTouchable(Touchable.enabled)
return false in stage listener or clear its listener.
do not call setInputProcessor(this.stage) in parent class instead call in child class.
I think the problem is because the touchDown() method of the stage returns true, and so the event will be "consumed" and won't be propagated to its children, the actors, and so the touchDown() method of boardImg will not be called.
Image doesn't receive touch events because it has no size.
Read documentation carefully:
Note that the actor must specify its bounds in order to receive input
events within those bounds.
Just know that boardImg = new Image(board) won't set width and height of actor for you. So you need to make it manualy. Assume you work with pixel perfect sizes:
boardImg = new Image(board);
boardImg.setWidth(board.getWidth());
boardImg.setHeight(board.getHeight());
That will do the trick. Good luck.