package com.github.czyzby.lml.parser.impl.tag.listener;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.utils.IntSet;
import com.github.czyzby.lml.parser.LmlParser;
import com.github.czyzby.lml.parser.impl.tag.AbstractListenerLmlTag;
import com.github.czyzby.lml.parser.tag.LmlActorBuilder;
import com.github.czyzby.lml.parser.tag.LmlTag;
/** Attachable tag. Can be a child of any tag and can have any actor tag children. Adds a {@link InputListener} to the
* parent actor. Each time the selected keys are pressed (or any key is pressed, if they are not chosen), actors that
* are children of listener tag will be added to the stage. Useful for "delayed" adding of actors - for example, one can
* easily define a dialog that will be shown each time a certain key is typed. Note that is not simply an equivalent of
* {@link com.github.czyzby.lml.parser.impl.tag.macro.InputListenerLmlMacroTag input listener macro}: macro parses
* template between its tags after event occurs (so actor creation is delayed). When using this tag, actors are created
* along the rest of the template - they are simply not added to the stage immediately, but otherwise they are parsed
* like any other actors and can access local variables.
*
* <p>
* Mapped to "onInput", "inputListener".
*
* @author MJ
* @see #setCondition(String) */
public class InputListenerLmlTag extends AbstractListenerLmlTag {
private KeysListener listener;
private IntSet keys;
public InputListenerLmlTag(final LmlParser parser, final LmlTag parentTag, final StringBuilder rawTagData) {
super(parser, parentTag, rawTagData);
}
@Override
protected Actor getNewInstanceOfActor(final LmlActorBuilder builder) {
keys = new IntSet();
listener = new KeysListener(keys) {
@Override
protected void handleEvent(final Actor actor) {
doOnEvent(actor);
}
};
return super.getNewInstanceOfActor(builder);
}
@Override
protected InputListener getEventListener() {
return listener;
}
/** @param keyCode key code from {@link Keys}. Will trigger the event. If no codes are added, event is processed
* after every key. */
public void addKey(final int keyCode) {
keys.add(keyCode);
}
/** @return direct reference to handled keys.
* @see #addKey(int) */
public IntSet getKeys() {
return keys;
}
/** @param combined if true, all keys have to be pressed at the same to process the event.
* @see #addKey(int) */
public void setCombined(final boolean combined) {
listener.setCombined(combined);
}
/** Listens to certain keys or key combinations.
*
* @author MJ */
public static abstract class KeysListener extends InputListener {
private final IntSet keys;
private boolean combined;
private final IntSet pressed = new IntSet();
/** @param keys supported keys. */
public KeysListener(final IntSet keys) {
this.keys = keys;
}
public void setCombined(final boolean combined) {
this.combined = combined;
}
@Override
public boolean keyDown(final InputEvent event, final int keycode) {
if (keys.size == 0) {
handleEvent(event.getListenerActor());
return true;
}
if (keys.contains(keycode)) {
if (!combined) {
handleEvent(event.getListenerActor());
return true;
}
pressed.add(keycode);
if (keys.size == pressed.size) {
handleEvent(event.getListenerActor());
pressed.clear();
}
return true;
}
return false;
}
@Override
public boolean keyUp(final InputEvent event, final int keycode) {
if (keys.contains(keycode)) {
pressed.remove(keycode);
return true;
}
return false;
}
/** @param actor has the listener attached. */
protected abstract void handleEvent(Actor actor);
}
}