package squidpony.squidgrid.gui.gdx;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.CharArray;
import com.badlogic.gdx.utils.viewport.StretchViewport;
import java.util.Map;
import java.util.TreeMap;
/**
* Acts like SquidInput but displays available keys as visible buttons if the device has no physical keyboard.
* Only relevant if you want keyboard input to always be available, so this doesn't implement the SquidInput
* constructors that don't handle any keys (the buttons this shows will act like keys even on Android and iOS).
* Created by Tommy Ettinger on 4/15/2016.
*/
public class VisualInput extends SquidInput {
public SquidPanel left, right;
protected TextCellFactory tcfLeft, tcfRight;
protected TreeMap<Character, String> availableKeys;
protected SquidMouse mouseLeft, mouseRight;
private int sectionWidth, sectionHeight;
private float screenWidth = -1, screenHeight = -1;
protected boolean initialized = false;
public Color color = Color.WHITE;
protected CharArray clicks;
public boolean eightWay = true, forceButtons = false;
public Stage stage;
protected ShrinkPartViewport spv;
private void init()
{
initialized = true;
sectionWidth = Gdx.graphics.getWidth() / 8;
sectionHeight = Gdx.graphics.getHeight();
tcfLeft = DefaultResources.getStretchableFont().width(sectionWidth / 4).height(sectionHeight / 16).initBySize();
tcfRight = DefaultResources.getStretchableFont().width(sectionWidth / 12).height(sectionHeight / 24).initBySize();
left = new SquidPanel(4, 16, tcfLeft);
if(eightWay) {
left.put(0, 7, new char[][]{
new char[]{'\\', '-', '/'},
new char[]{'|', 'O', '|'},
new char[]{'/', '-', '\\'},
}, color);
}
else
{
left.put(0, 7, new char[][]{
new char[]{' ', SquidInput.LEFT_ARROW, ' '},
new char[]{SquidInput.UP_ARROW, 'O', SquidInput.DOWN_ARROW},
new char[]{' ', SquidInput.RIGHT_ARROW, ' '},
}, color); }
right = new SquidPanel(12, 24, tcfRight, null, Gdx.graphics.getWidth() - sectionWidth, 0);
mouseLeft = new SquidMouse(left.cellWidth(), left.cellHeight(), left.gridWidth, left.gridHeight,
0, 0, new InputAdapter()
{
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if(screenX < 3 && screenY >= 5 && screenY <= 7 && left.contents[screenX][screenY + 2] != 0 &&
left.contents[screenX][screenY + 2] != ' ')
{
switch ((screenY - 5) * 3 + screenX)
{
case 0: queue.add(SquidInput.UP_LEFT_ARROW);
break;
case 1: queue.add(SquidInput.UP_ARROW);
break;
case 2: queue.add(SquidInput.UP_RIGHT_ARROW);
break;
case 3: queue.add(SquidInput.LEFT_ARROW);
break;
case 4: queue.add(SquidInput.CENTER_ARROW);
break;
case 5: queue.add(SquidInput.RIGHT_ARROW);
break;
case 6: queue.add(SquidInput.DOWN_LEFT_ARROW);
break;
case 7: queue.add(SquidInput.DOWN_ARROW);
break;
case 8: queue.add(SquidInput.DOWN_RIGHT_ARROW);
break;
default:
return false;
}
queue.add('\u0000');
return true;
}
else
return false;
}
});
//if(mouse != null)
// mouse.setOffsetX(-sectionWidth);
stage = new Stage(new StretchViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()));
stage.addActor(left);
stage.addActor(right);
}
private void fillActions()
{
if(!initialized)
return;
int y = 0;
clicks = new CharArray(right.getGridHeight());
for(Map.Entry<Character, String> kv : availableKeys.entrySet())
{
switch (kv.getKey())
{
case SquidInput.UP_LEFT_ARROW:
case SquidInput.UP_ARROW:
case SquidInput.UP_RIGHT_ARROW:
case SquidInput.LEFT_ARROW:
case SquidInput.CENTER_ARROW:
case SquidInput.RIGHT_ARROW:
case SquidInput.DOWN_LEFT_ARROW:
case SquidInput.DOWN_ARROW:
case SquidInput.DOWN_RIGHT_ARROW:
break;
default:
right.put(1, y, kv.getValue(), color);
clicks.add(kv.getKey());
y++;
}
if(y > right.getGridHeight())
break;
}
mouseRight = new SquidMouse(right.cellWidth(), right.cellHeight(), right.gridWidth(), right.gridHeight(),
Gdx.graphics.getWidth() - sectionWidth, Math.round(sectionHeight - right.getHeight()),
new InputAdapter()
{
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if(screenY < clicks.size)
{
queue.add(clicks.get(screenY));
queue.add('\u0000');
return true;
}
return false;
}
});
}
/**
* Convenience method that does essentially the same thing as init(Map<Character, String>).
* This assumes that each String passed to this is an action, and the first letter of a String is the character that
* a keypress would generate to perform the action named by the String. For example, calling this with
* {@code init("fire", "Fortify")} would register "fire" under 'f' and "Fortify" under 'F', displaying the Strings
* instead of the characters if possible. The first char of each String should be unique in the arguments.
* <br>
* This also initializes the displayed buttons if there is no hardware keyboard available. This uses the color and
* eightWay fields to determine what color the buttons will be drawn with and if directions should be 8-way or
* 4-way, respectively. These fields should be set before calling init() if you don't want the defaults, which are
* white buttons and 8-way directions.
* @param enabled an array or vararg of Strings that name actions; the first char of each String should be unique
*/
public void init(String... enabled)
{
if(!forceButtons && Gdx.input.isPeripheralAvailable(Input.Peripheral.HardwareKeyboard))
return;
init();
availableKeys = new TreeMap<>();
if(enabled == null)
return;
for (int i = 0; i < enabled.length; i++) {
if(enabled[i] != null && !enabled[i].isEmpty())
availableKeys.put(enabled[i].charAt(0), enabled[i]);
}
fillActions();
}
/**
* For each char and String in available, registers each keyboard key (as a char, such as 'A' or
* SquidInput.LEFT_ARROW) with a String that names the action that key is used to perform, and makes these Strings
* available as buttons on the right side of the screen if on a device with no hardware keyboard. Arrows will be
* provided on the left side of the screen for directions.
* <br>
* This uses the color and eightWay fields to determine what color the buttons will be drawn with and if directions
* should be 8-way or 4-way, respectively. These fields should be set before calling init() if you don't want the
* defaults, which are white buttons and 8-way directions.
* @param available a Map of Character keys representing keyboard keys and Strings for the actions they trigger
*/
public void init(Map<Character, String> available)
{
if(!forceButtons && Gdx.input.isPeripheralAvailable(Input.Peripheral.HardwareKeyboard))
return;
init();
if(available != null) {
availableKeys = new TreeMap<>(available);
fillActions();
}
}
private VisualInput() {
}
public VisualInput(KeyHandler keyHandler) {
super(keyHandler);
}
public VisualInput(KeyHandler keyHandler, boolean ignoreInput) {
super(keyHandler, ignoreInput);
}
public VisualInput(KeyHandler keyHandler, SquidMouse mouse) {
super(keyHandler, mouse);
}
public VisualInput(KeyHandler keyHandler, SquidMouse mouse, boolean ignoreInput) {
super(keyHandler, mouse, ignoreInput);
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (initialized && mouseLeft.onGrid(screenX, screenY))
return mouseLeft.touchDown(screenX, screenY, pointer, button);
else if (initialized && mouseRight.onGrid(screenX, screenY))
return mouseRight.touchDown(screenX, screenY, pointer, button);
if(spv != null) {
screenX *= spv.getScreenWidth() / (spv.getScreenWidth() - spv.barWidth);
}
return (!initialized || mouse.onGrid(screenX, screenY)) && super.touchDown(screenX, screenY, pointer, button);
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if (initialized && mouseLeft.onGrid(screenX, screenY))
return mouseLeft.touchUp(screenX, screenY, pointer, button);
else if (initialized && mouseRight.onGrid(screenX, screenY))
return mouseRight.touchUp(screenX, screenY, pointer, button);
if(spv != null) {
screenX *= spv.getScreenWidth() / (spv.getScreenWidth() - spv.barWidth);
}
return (!initialized || mouse.onGrid(screenX, screenY)) && super.touchUp(screenX, screenY, pointer, button);
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
if (initialized && mouseLeft.onGrid(screenX, screenY))
return mouseLeft.touchDragged(screenX, screenY, pointer);
else if (initialized && mouseRight.onGrid(screenX, screenY))
return mouseRight.touchDragged(screenX, screenY, pointer);
if(spv != null) {
screenX *= spv.getScreenWidth() / (spv.getScreenWidth() - spv.barWidth);
}
return (!initialized || mouse.onGrid(screenX, screenY)) && super.touchDragged(screenX, screenY, pointer);
}
@Override
public boolean mouseMoved (int screenX, int screenY) {
if(spv != null) {
screenX *= spv.getScreenWidth() / (spv.getScreenWidth() - spv.barWidth);
}
if(ignoreInput || !mouse.onGrid(screenX, screenY)) return false;
return mouse.mouseMoved(screenX, screenY);
}
public void reinitialize(float cellWidth, float cellHeight, float gridWidth, float gridHeight,
int offsetX, int offsetY, float screenWidth, float screenHeight)
{
if(!initialized)
{
mouse.reinitialize(cellWidth, cellHeight, gridWidth, gridHeight, offsetX, offsetY);
return;
}
if(this.screenWidth > 0)
sectionWidth *= screenWidth / this.screenWidth;
else
sectionWidth *= screenWidth / Gdx.graphics.getWidth();
if(this.screenHeight > 0)
sectionHeight *= screenHeight / this.screenHeight;
else
sectionHeight *= screenHeight / Gdx.graphics.getHeight();
cellWidth /= screenWidth / (screenWidth - sectionWidth);
float leftWidth = screenWidth / 8f / 4f, rightWidth = screenWidth / 8f / 12f,
leftHeight = screenHeight / 12f, rightHeight = screenHeight / 24f;
mouse.reinitialize(cellWidth, cellHeight, gridWidth, gridHeight,
offsetX - MathUtils.round((screenWidth / 8f) * (screenWidth / (screenWidth - sectionWidth)) + cellWidth /2f), offsetY);
mouseLeft.reinitialize(leftWidth, leftHeight, 4, 16, offsetX, offsetY);
mouseRight.reinitialize(rightWidth, rightHeight, 12, 24,
MathUtils.ceil(offsetX - (screenWidth - sectionWidth)),
MathUtils.round(offsetY - rightHeight / 2f + (right.getGridHeight() * rightHeight - screenHeight)));
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
if(spv != null)
spv.barWidth = sectionWidth;
}
public ShrinkPartViewport resizeInnerStage(Stage insides)
{
if(!initialized)
return null;
/*
insides.getViewport().setWorldWidth(insides.getViewport().getWorldWidth() - screenWidth * 2);
insides.getViewport().setScreenX(screenWidth);
insides.getViewport().setScreenY(0);
*/
spv = new ShrinkPartViewport(insides.getWidth(), insides.getHeight(), sectionWidth);
insides.setViewport(spv);
return spv;
}
public int getSectionWidth() {
return sectionWidth;
}
public int getSectionHeight() {
return sectionHeight;
}
public void update(int width, int height, boolean centerCamera) {
if(initialized)
{
stage.getViewport().update(width, height, centerCamera);
}
}
public void show() {
if(initialized) {
stage.getViewport().apply(true);
stage.draw();
stage.act();
}
}
}