Libgdx Mouse just clicked - java

I'm trying to get when the mouse just clicked, not when the mouse is pressed.
I mean I use a code in a loop and if I detect if the mouse is pressed the code will execute a lot of time, but I want execute the code only Once, when the mouse has just clicked.
This is my code :
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
//Some stuff
}

See http://code.google.com/p/libgdx/wiki/InputEvent - you need to handle input events instead of polling, by extending InputProcessor and passing your custom input processor to Gdx.input.setInputProcessor().
EDIT:
public class MyInputProcessor implements InputProcessor {
#Override
public boolean touchDown (int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
// Some stuff
return true;
}
return false;
}
}
And wherever you want to use that:
MyInputProcessor inputProcessor = new MyInputProcessor();
Gdx.input.setInputProcessor(inputProcessor);
If find it easier to use this pattern:
class AwesomeGameClass {
public void init() {
Gdx.input.setInputProcessor(new InputProcessor() {
#Override
public boolean TouchDown(int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
onMouseDown();
return true;
}
return false
}
... the other implementations for InputProcessor go here, if you're using Eclipse or Intellij they'll add them in automatically ...
});
}
private void onMouseDown() {
}
}

You can use Gdx.input.justTouched(), which is true in the first frame where the mouse is clicked. Or, as the other answer states, you can use an InputProcessor (or InputAdapter) and handle the touchDown event:
Gdx.input.setInputProcessor(new InputAdapter() {
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (button == Buttons.LEFT) {
// do something
}
}
});

Without InputProcessor you can use easy like this in your render loop:
#Override
public void render(float delta) {
if(Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)){
//TODO:
}
}

Related

JButton appearance as if it were pressed

In some cases a need my JButton to appear as if it were pressed. This depends on some boolean.
I tried to create my own ButtonModel that overrides the default isPressed() method but in that way the button appears pressed only in case the mouse pointer is on top of it (without pressing a mouse button). I need it to appear pressed also if the mouse is somewhere else.
So far I tried this:
class MyButtonModel extends DefaultButtonModel
{
private boolean appearPressed;
#Override
public boolean isPressed()
{
return super.isPressed() || appearPressed;
}
}
I cannot use a JToggleButton or something similar.
My button derives from another class that implements some additional features and derives itself from JButton.
UPDATE:
I'm running on Windows 10 and use the WindowsClassic Look&Feel.
you also need to override isArmed():
class MyButtonModel extends DefaultButtonModel
{
private boolean appearPressed = true;
#Override
public boolean isPressed()
{
return super.isPressed() || appearPressed;
}
#Override
public boolean isArmed() {
return super.isArmed() || appearPressed;
}
}
The partial reply was given by #Philipp Li and #camickr. Thank you.
My button model needs to override isArmed() in order to obtain the desired result.
However, once the button is pressed, it doesn't respond anymore.
Digging in the source of DefaultButtonModel, I found this:
public void setPressed(boolean b)
{
if ((isPressed() == b) || (!isEnabled()))
return;
...
}
This method is invoked by AbstractButton.doClick() in order to notify the various ActionListeners.
In other words, if the overridden method isPressed() returns true because I want to make the button appear pressed, a successive click on that button will be ignored.
A similar thing occurs within DefaultButtonModel.setArmed().
Therefore, I implemented my button model as follows:
class MyButtonModel extends DefaultButtonModel
{
boolean appearPressed = false;
private boolean withinSetPressedMethod = false;
private boolean withinSetArmedMethod = false;
#Override
public void setPressed(boolean pressed)
{
withinSetPressedMethod = true;
super.setPressed(pressed);
withinSetPressedMethod = false;
}
#Override
public void setArmed(boolean armed)
{
withinSetArmedMethod = true;
super.setArmed(armed);
withinSetArmedMethod = false;
}
#Override
public boolean isPressed()
{
if (withinSetPressedMethod)
return (super.isPressed());
else
return (super.isPressed() || appearPressed);
}
#Override
public boolean isArmed()
{
if (withinSetArmedMethod)
return (super.isArmed());
else
return (super.isArmed() || appearPressed);
}
} // class MyButtonModel

How to continue sequence / parallel actions from cachedActions Libgdx

I have a clicklistener extended class which aims to cached any current actions of the actor during touchDown, and assigns it back when touchUp is triggered. However, it does not works for sequence or parallel actions.
public class MyClickListener extends ClickListener {
public Actor actor;
private final Array<Action> cachedActions = new Array<Action>();
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
super.touchUp(event, x, y, pointer, button);
actor = event.getListenerActor();
actor.addAction(btnScaleBackActions());
for(Action action:cachedActions)
{
//action.reset(); // i wants the actor to continue at where it stop
action.setTarget(actor);
action.setActor(actor);
actor.addAction(action);
}
cachedActions.clear();
}
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if(pointer==0)
{
actor = event.getListenerActor();
actor.setScale(0.9f);
cachedActions.addAll(actor.getActions());
actor.clearActions();
return super.touchDown(event, x, y, pointer, button);
}
else
{
return false;
}
}
My buttons testing:
// button touchUp continue its previous action at where it stop
btn1.addAction(Actions.scaleBy(1,1,3));
// button touchUp not continue it previous actions and complete stop
btn2.addAction(sequence(Actions.scaleBy(1,1,3)));
// button touchUp give nullException error
btn3.addAction(forever(Actions.scaleBy(1,1,3)));
//error :
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.badlogic.gdx.scenes.scene2d.actions.RepeatAction.delegate(RepeatAction.java:29)
at com.badlogic.gdx.scenes.scene2d.actions.DelegateAction.act(DelegateAction.java:43)
Is it possible to continue sequence/parallel actions at where it stop at myClickListener class?
Here's an alternate idea. Rather than deal with removing and restoring actions, and subsequently dealing with the pools issue, you can wrap your actions in a new type of pausable action.
public class PausableAction extends DelegateAction {
public static PausableAction pausable(Action wrappedAction){
PausableAction action = Actions.action(PausableAction.class);
action.setAction(wrappedAction);
return action;
}
boolean paused = false;
public void pause (){
paused = true;
}
public void unpause (){
paused = false;
}
protected boolean delegate (float delta){
if (paused)
return false;
return action.act(delta);
}
public void restart () {
super.restart();
paused = false;
}
}
Now when getting your actions, wrap them in a pausable, for example:
btn1.addAction(PausableAction.pausable(Actions.scaleBy(1,1,3)));
And pause/unpause actions when you need to, like:
//...
actor = event.getListenerActor();
actor.setScale(0.9f);
for (Action action : actor.getActions())
if (action instanceof PausableAction)
((PausableAction)action).pause();
return super.touchDown(event, x, y, pointer, button);
The default behavior of actions that came from a pool (like from the Actions class) is to restart themselves when they are removed from an actor. It's actually not safe for you to be reusing these instances because they have also been returned to the pool and might get attached to some other actor unexpectedly.
So before you remove them from your actor, you need to set their pools to null.
private static void clearPools (Array<Action> actions){
for (Action action : actions){
action.setPool(null);
if (action instanceof ParallelAction) //SequenceActions are also ParallelActions
clearPools(((ParallelAction)action).getActions());
else if (action instanceof DelegateAction)
((DelegateAction)action).getAction().setPool(null);
}
}
//And right before actor.clearActions();
clearPools(actor.getActions());
Then, when you add them back to the actor, you'll want to add their pools back so they can go back to the Actions pools and be reused later to avoid GC churn.
private static void assignPools (Array<Action> actions){
for (Action action : actions){
action.setPool(Pools.get(action.getClass()));
if (action instanceof ParallelAction)
assignPools(((ParallelAction)action).getActions());
else if (action instanceof DelegateAction){
Action innerAction = ((DelegateAction)action).getAction();
innerAction.setPool(Pools.get(innerAction.getClass()));
}
}
}
//And call it on your actor right after adding the actions back:
assignPools(actor.getActions);

MouseListener and KeyListener used at the same time

How do I use MouseListener and KeyListener at the same time?
For example, how to do something like this
public void keyPressed( KeyEvent e){
// If the mouse button is clicked while any key is held down, print the key
}
Use a boolean value for whether a mouse button is held down, and then update that variable in a MouseListener.
boolean buttonDown = false;
public class ExampleListener implements MouseListener {
public void mousePressed(MouseEvent e) {
buttonDown = true;
}
public void mouseReleased(MouseEvent e) {
buttonDown = false;
}
//Other implemented methods unimportant to this post...
}
Then, in your KeyListener class, just test the buttonDown variable.
Try creating a boolean isKeyPressed. In keyPressed, set it to true and in keyReleased set it to false. Then, when the mouse is clicked, first check if isKeyPressed is true.
You could try checking the state of the KeyEvent modifiers, for example...
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
int mods = e.getModifiers();
System.out.println(mods);
if ((mods & KeyEvent.BUTTON1_MASK) != 0) {
System.out.println("Button1 down");
}
}
});
I should also point out, you can do...
int ext = e.getModifiersEx();
if ((ext & KeyEvent.BUTTON1_DOWN_MASK) != 0) {
System.out.println("Button1_down_mask");
}
Which, as I've just discovered, produces results for other mouse buttons...

Libgdx override keyDown using InputMultiplexer in Screen

I have a screen and have created a new multiplexer and set an input processor.
How can I override keyDown on my screen?
public class Screen implements Screen, TextInputListener
{
...stuff...
public Screen(Game game)
{
multiplexer = new InputMultiplexer();
multiplexer.addProcessor(mySystem);
multiplexer.addProcessor(aStage);
Gdx.input.setInputProcessor(multiplexer);
}
...more stuff....
}
Thanks
To override KeyDown you need to implement InputProcessor.
For example if you want to override the Android Back button and the Backspace button on PC you can use a class like this.
public class ScreenInputHandler implements InputProcessor {
#Override
public boolean keyDown(int keycode) {
if(keycode == Keys.BACK || keycode == Keys.BACKSPACE){
...code here
}
return false;
}
}
Remember add the input handler to your InputMultiplexer.
This is how I ended up solving it:
InputProcessor backProcessor = new InputAdapter()
{
#Override
public boolean keyDown(int keycode)
{
//do stuff
return false;
}
};
multiplexer.addProcessor(backProcessor);

Enter key handling in Libgdx TextField

I set up a stage with three TextFields in my libgdx app, and I get different behaviour in the desktop mode and the Android mode. On Android, typing the enter key moves the cursor to the next TextField. On the desktop, typing the enter key does nothing.
How can I make the cursor move consistently on both platforms? I want to be able to set the focus to another field when the user types enter. On Android, whatever I set the focus to, the default enter key behaviour then jumps the focus to the field after that.
Here's the code I'm currently using to move the cursor and clear the next field:
stage.addListener(new InputListener() {
#Override
public boolean keyUp(InputEvent event, int keycode) {
if (keycode == Input.Keys.ENTER) {
nextField();
}
return false;
}
});
Gdx.input.setInputProcessor(stage);
}
private void nextField() {
TextField nextField =
stage.getKeyboardFocus() == text1
? text2
: stage.getKeyboardFocus() == text2
? text3
: text1;
nextField.setText("");
stage.setKeyboardFocus(nextField);
}
I've tried cancelling the event or returning true from the handler methods, but the focus still moves after my code finishes.
My complete sample code is on GitHub.
TextField uses a private internal InputListener, that gets initialized in the constructor and cannot be easily overwritten. The relevant code that changes the focus is during the keyTyped method of this listener:
public boolean keyTyped (InputEvent event, char character) {
[...]
if ((character == TAB || character == ENTER_ANDROID) && focusTraversal)
next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
[...]
}
One easy solution would be to disable focus traversals all together and set a com.badlogic.gdx.scenes.scene2d.ui.TextFieldListener that automatically does the traversals instead:
TextField textField
textField.setFocusTraversal(false);
textField.setTextFieldListener(new TextFieldListener() {
#Override
public void keyTyped(TextField textField, char key) {
if ((key == '\r' || key == '\n')){
textField.next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
}
}
});
If you need to be able to enable and disable focus traversals using TextFields setFocusTraversal method, there would also be a quite hacky solution by wrapping the internal InputListener inside your own listener when it is added to the TextField (but I would not recommend this):
class MyTextField extends TextField{
class InputWrapper extends InputListener{
private final InputListener l;
public InputWrapper(InputListener l) {
super();
this.l = l;
}
#Override
public boolean handle(Event e) {
return l.handle(e);
}
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
return l.touchDown(event, x, y, pointer, button);
}
#Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
l.touchUp(event, x, y, pointer, button);
}
#Override
public void touchDragged(InputEvent event, float x, float y,
int pointer) {
l.touchDragged(event, x, y, pointer);
}
#Override
public boolean mouseMoved(InputEvent event, float x, float y) {
return l.mouseMoved(event, x, y);
}
#Override
public void enter(InputEvent event, float x, float y, int pointer,
Actor fromActor) {
l.enter(event, x, y, pointer, fromActor);
}
#Override
public void exit(InputEvent event, float x, float y, int pointer,
Actor toActor) {
l.exit(event, x, y, pointer, toActor);
}
#Override
public boolean scrolled(InputEvent event, float x, float y,
int amount) {
return l.scrolled(event, x, y, amount);
}
#Override
public boolean keyDown(InputEvent event, int keycode) {
return l.keyDown(event, keycode);
}
#Override
public boolean keyUp(InputEvent event, int keycode) {
return l.keyUp(event, keycode);
}
#Override
public boolean keyTyped(InputEvent event, char character) {
if (isDisabled()) {
return false;
} else if ((character == '\r' || character == '\n')){
next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
return true;
}
return l.keyTyped(event, character);
}
}
public MyTextField(String text, Skin skin, String styleName) {
super(text, skin, styleName);
}
public MyTextField(String text, Skin skin) {
super(text, skin);
}
public MyTextField(String text, TextFieldStyle style) {
super(text, style);
}
boolean initialized = false;
#Override
public boolean addListener (EventListener l) {
if (!initialized) {
if (!(l instanceof InputListener)) {
throw new IllegalStateException();
}
initialized = true;
return super.addListener(new InputWrapper((InputListener) l));
}
return super.addListener(l);
}
}
edit:
On a second thought, you could also do this with the first solution by simply overwriting setFocusTraversal of the TextField and enabling/disabling your own listener during calls to this method.
I found a workaround, but I'd still appreciate a cleaner solution that gets both platforms to behave the same way.
I added a flag to indicate whether the focus will move by default, and I only change the focus if it won't move on its own. I then set that flag from the MainActivity class for Android or the Main class for desktop. I've posted the complete sample code on GitHub.
private void nextField() {
TextField nextField =
stage.getKeyboardFocus() == text1
? text2
: stage.getKeyboardFocus() == text2
? text3
: text1;
nextField.setText("");
if ( ! isFocusMovedAutomatically) {
stage.setKeyboardFocus(nextField);
}
}
In gdx-1.9.4 I was able to do the following:
final TextField newMessageTextField = new TextField("", uiSkin){
#Override
protected InputListener createInputListener () {
return new TextFieldClickListener(){
#Override
public boolean keyUp(com.badlogic.gdx.scenes.scene2d.InputEvent event, int keycode) {
System.out.println("event="+event+" key="+keycode);
return super.keyUp(event, keycode);
};
};
}
};
After pressing the arrows with focus on the TextField I've got
event=keyUp key=19
event=keyUp key=20
event=keyUp key=22
event=keyUp key=21

Categories