package org.terasology.input;
import java.util.List;
import org.terasology.entitySystem.EntityRef;
import org.terasology.logic.manager.GUIManager;
import com.google.common.collect.Lists;
/**
* A BindableButton is pseudo button that is controlled by one or more actual inputs (whether keys, mouse buttons or the
* mouse wheel).
* <p/>
* When the BindableButton changes state it sends out events like an actual key or button does. It also allows direct
* subscription via the {@link BindButtonSubscriber} interface.
*/
public class BindableButtonImpl implements BindableButton {
private String id;
private String displayName;
private BindButtonEvent buttonEvent;
private int activeInputs = 0;
private List<BindButtonSubscriber> subscribers = Lists.newArrayList();
private ActivateMode mode = ActivateMode.BOTH;
private boolean repeating = false;
private int repeatTime = 0;
private long lastActivateTime;
/**
* Creates the button. Package-private, as should be created through the InputSystem
*
* @param id
* @param event
*/
BindableButtonImpl(String id, String displayName, BindButtonEvent event) {
this.id = id;
this.displayName = displayName;
this.buttonEvent = event;
}
@Override
public String getId() {
return id;
}
@Override
public String getDisplayName() {
return displayName;
}
@Override
public void setMode(ActivateMode mode) {
this.mode = mode;
}
@Override
public ActivateMode getMode() {
return mode;
}
@Override
public void setRepeating(boolean repeating) {
this.repeating = repeating;
}
@Override
public boolean isRepeating() {
return repeating;
}
/**
* Sets the repeat time
*
* @param repeatTimeMs The time between repeat events, in ms
*/
@Override
public void setRepeatTime(int repeatTimeMs) {
this.repeatTime = repeatTimeMs;
}
@Override
public int getRepeatTime() {
return repeatTime;
}
@Override
public ButtonState getState() {
return (activeInputs > 0) ? ButtonState.DOWN : ButtonState.UP;
}
/**
* Register a subscriber to this bind
*
* @param subscriber
*/
@Override
public void subscribe(BindButtonSubscriber subscriber) {
subscribers.add(subscriber);
}
/**
* Removes a subscriber from this bind
*
* @param subscriber
*/
@Override
public void unsubscribe(BindButtonSubscriber subscriber) {
subscribers.remove(subscriber);
}
/**
* Updates this bind with the new state of a bound button. This should be done whenever a bound button changes
* state, so that the overall state of the bind can be tracked.
*
* @param pressed Is the changing
* @param delta The length of the current frame
* @param target The current camera target
* @param keyConsumed Has the changing button's event already been consumed
* @param guiOnly Is the gui consuming input
* @return Whether the button's event has been consumed
*/
boolean updateBindState(boolean pressed, float delta, EntityRef target, EntityRef localPlayer, boolean keyConsumed, boolean guiOnly) {
if (pressed) {
activeInputs++;
if (activeInputs == 1 && mode.isActivatedOnPress()) {
lastActivateTime = System.currentTimeMillis();
if (guiOnly) {
GUIManager.getInstance().processBindButton(id, pressed);
keyConsumed = true;
}
if (!keyConsumed) {
keyConsumed = triggerOnPress(delta, target);
}
if (!keyConsumed) {
buttonEvent.prepare(id, ButtonState.DOWN, delta, target);
localPlayer.send(buttonEvent);
keyConsumed = buttonEvent.isConsumed();
}
}
} else if (activeInputs != 0) {
activeInputs--;
if (activeInputs == 0 && mode.isActivatedOnRelease()) {
if (guiOnly) {
GUIManager.getInstance().processBindButton(id, pressed);
keyConsumed = true;
}
if (!keyConsumed) {
keyConsumed = triggerOnRelease(delta, target);
}
if (!keyConsumed) {
buttonEvent.prepare(id, ButtonState.UP, delta, target);
localPlayer.send(buttonEvent);
keyConsumed = buttonEvent.isConsumed();
}
}
}
return keyConsumed;
}
void update(EntityRef localPlayer, float delta, EntityRef target) {
long time = System.currentTimeMillis();
if (repeating && getState() == ButtonState.DOWN && mode.isActivatedOnPress() && time - lastActivateTime > repeatTime) {
lastActivateTime = time;
if (!GUIManager.getInstance().isConsumingInput()) {
boolean consumed = triggerOnRepeat(delta, target);
if (!consumed) {
buttonEvent.prepare(id, ButtonState.REPEAT, delta, target);
localPlayer.send(buttonEvent);
}
}
}
}
private boolean triggerOnPress(float delta, EntityRef target) {
for (BindButtonSubscriber subscriber : subscribers) {
if (subscriber.onPress(delta, target)) {
return true;
}
}
return false;
}
private boolean triggerOnRepeat(float delta, EntityRef target) {
for (BindButtonSubscriber subscriber : subscribers) {
if (subscriber.onRepeat(delta, target)) {
return true;
}
}
return false;
}
private boolean triggerOnRelease(float delta, EntityRef target) {
for (BindButtonSubscriber subscriber : subscribers) {
if (subscriber.onRelease(delta, target)) {
return true;
}
}
return false;
}
}