Moves many of the GUI tasks for CalibrateInputScreen to their own class.

I plan on reusing them in JoystickPreviewScreen.
This commit is contained in:
Markil3
2020-12-18 10:28:11 -07:00
parent e2a8d1304b
commit 310356b1eb
2 changed files with 275 additions and 136 deletions

View File

@@ -0,0 +1,200 @@
package markil3.controller;
import com.jme3.asset.AssetManager;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.font.Rectangle;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Quad;
/**
* A collection of utility methods for the screens used here.
* @author Markil 3
* @version 1.1
*/
public class GUIUtils
{
/**
* The color that a button takes on when the mouse is down on it. Feel
* free to change it to suit your needs.
*/
static ColorRGBA BUTTON_COLOR_ON = new ColorRGBA(0F, 0.1F, 0F, 1F);
/**
* The default color that a button. Feel free to change it to suit your
* needs.
*/
static ColorRGBA BUTTON_COLOR_OFF = new ColorRGBA(0F, 0.75F, 0.25F, 1F);
private static final String BUTTON_ID = "button";
/**
* Creates a button. This button will have special user data fields that
* can be detected by {@link #handleButtonPress(Node, Vector2f, boolean)}
* to trigger events.
* @param assets - The application asset manager.
* @param guiFont - The font to use for the button.
* @param id - The button ID. This is used to identify which button was
* pressed later on.
* @param content - The text that will display in the button.
* @return A node containing the button elements.
*/
public static Node createButton(AssetManager assets, BitmapFont guiFont,
String id, String content)
{
BitmapText buttonText;
Geometry buttonBackground;
Node button = new Node();
buttonText = guiFont.createLabel(content);
buttonText.setUserData(BUTTON_ID, id);
buttonText.setBox(new Rectangle(0, 0, buttonText.getLineWidth(),
buttonText.getLineHeight()));
buttonText.setAlignment(BitmapFont.Align.Center);
buttonText.setVerticalAlignment(BitmapFont.VAlign.Center);
buttonBackground = new Geometry("button-" + id,
new Quad(buttonText.getLineWidth() + 10,
buttonText.getHeight() + 5));
buttonBackground.setMaterial(
new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md"));
buttonBackground.getMaterial().setColor("Color", BUTTON_COLOR_OFF);
buttonBackground.setUserData(BUTTON_ID, id);
buttonBackground
.setLocalTranslation(-2.5F, -buttonText.getLineHeight() - 2.5F,
0);
button.setUserData(BUTTON_ID, id);
button.attachChild(buttonBackground);
button.attachChild(buttonText);
return button;
}
/**
* Call this method from a mouse listener to search the provided node for
* any buttons created with
* {@link #createButton(AssetManager, BitmapFont, String, String)} and
* returns their ID.
* @param gui - The GUI to search.
* @param cursor - The position of the mouse cursor.
* @param isPressed - Whether or not the cursor is pressed. This will
* change how the button is displayed.
* @return The ID of the button that was clicked, or null if none were
* clicked.
*/
public static String handleButtonPress(Node gui, Vector2f cursor,
boolean isPressed)
{
final String KEY_CURRENT = "currentButton";
CollisionResults results;
Ray ray;
Geometry button;
String buttonId = null;
/*
* We generally mark buttons on mouse down, and trigger that same
* button on release. If we haven't even marked a button by the time
* we release the mouse, we just won't bother.
*/
if (!isPressed)
{
button = gui.getUserData(KEY_CURRENT);
if (button == null)
{
return null;
}
}
results = new CollisionResults();
ray = new Ray(new Vector3f(cursor.x, cursor.y, 0),
new Vector3f(cursor.x, cursor.y, 1));
gui.collideWith(ray, results);
for (CollisionResult result : results)
{
button = result.getGeometry();
buttonId = button.getUserData(BUTTON_ID);
if (buttonId != null)
{
if (isPressed)
{
/*
* Trigger the press graphic
*/
button.getMaterial().setColor("Color", BUTTON_COLOR_ON);
gui.setUserData(KEY_CURRENT, button);
break;
}
else
{
/*
* If the button we released isn't the same as the button
* we originally pressed, don't trigger either.
*/
button = gui.getUserData(KEY_CURRENT);
if (button != null)
{
if (button.getUserData(BUTTON_ID).equals(buttonId))
{
break;
}
else
{
buttonId = null;
}
}
}
}
}
if (!isPressed)
{
/*
* Make sure the originally-clicked button was reset.
*/
button = gui.getUserData(KEY_CURRENT);
if (button != null)
{
button.getMaterial().setColor("Color", BUTTON_COLOR_OFF);
gui.setUserData(KEY_CURRENT, null);
}
}
return buttonId;
}
/**
* Aligns the contents in a container so that all {@link BitmapText} and
* button elements will align in a column.
* @param cont - The container to align.
* @param width - The width of the screen.
* @param height - The height of the screen.
* @return The total height of the container. This is useful in
* positioning the container itself.
*/
public static float alignContainer(Node cont, int width, int height)
{
float totalHeight = 0;
for (Spatial node : cont.getChildren())
{
if (node instanceof BitmapText)
{
node.setLocalTranslation(
-((BitmapText) node).getLineWidth() / 2F, -totalHeight,
0);
totalHeight += ((BitmapText) node).getHeight();
}
else if (node.getUserData(BUTTON_ID) != null)
{
Quad quad = ((Quad) ((Geometry) ((Node) node).getChild(0))
.getMesh());
node.setLocalTranslation(-quad.getWidth() / 2F,
-totalHeight - 10, 0);
totalHeight += quad.getHeight() + 10;
}
}
return totalHeight;
}
}

View File

@@ -3,7 +3,6 @@ package markil3.controller;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
@@ -21,11 +20,9 @@ import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
@@ -62,6 +59,7 @@ import static markil3.controller.Main.CALIBRATION_FILE;
/**
* Provides a series of prompts that will build a controller calibration file.
* @author Markil3
* @version 1.1
*/
public class CalibrateInputScreen extends BaseAppState
implements RawInputListener, ActionListener
@@ -155,32 +153,6 @@ public class CalibrateInputScreen extends BaseAppState
private HashMap<Object, Float> defaultValues = new HashMap<>();
private Node createButton(String id, String content)
{
BitmapText buttonText;
Geometry buttonBackground;
Node button = new Node();
buttonText = this.guiFont.createLabel(content);
buttonText.setUserData("button", id);
buttonText.setBox(new Rectangle(0, 0, buttonText.getLineWidth(), buttonText.getLineHeight()));
buttonText.setAlignment(BitmapFont.Align.Center);
buttonText.setVerticalAlignment(BitmapFont.VAlign.Center);
buttonBackground = new Geometry("button-" + id,
new Quad(buttonText.getLineWidth() + 10,
buttonText.getLineHeight() + 5));
buttonBackground.setMaterial(
new Material(this.getApplication().getAssetManager(),
"Common/MatDefs/Misc/Unshaded.j3md"));
buttonBackground.getMaterial()
.setColor("Color", HIGHLIGHTED_BUTTON_COLOR);
buttonBackground.setUserData("button", id);
buttonBackground.setLocalTranslation(-2.5F, -buttonText.getLineHeight() - 2.5F, 0);
button.setUserData("button", id);
button.attachChild(buttonBackground);
button.attachChild(buttonText);
return button;
}
@Override
protected void initialize(Application app)
{
@@ -207,7 +179,9 @@ public class CalibrateInputScreen extends BaseAppState
text.setVerticalAlignment(BitmapFont.VAlign.Center);
this.introCont.attachChild(text);
this.startButton = this.createButton("start", "Start");
this.startButton =
GUIUtils.createButton(app.getAssetManager(), this.guiFont,
"start", "Start");
// this.startButton.addClickCommands(this);
this.introCont.attachChild(this.startButton);
@@ -217,12 +191,18 @@ public class CalibrateInputScreen extends BaseAppState
"First, press any button or axis\n" +
"on the controller you want to calibrate."));
this.skipButton = this.createButton("skip", "Skip");
this.skipButton =
GUIUtils.createButton(app.getAssetManager(), this.guiFont,
"skip", "Skip");
this.cancelButton = this.createButton("cancel", "Cancel");
this.cancelButton =
GUIUtils.createButton(app.getAssetManager(), this.guiFont,
"cancel", "Cancel");
this.introCont.attachChild(this.cancelButton);
this.restartButton = this.createButton("close", "Close Application");
this.restartButton =
GUIUtils.createButton(app.getAssetManager(), this.guiFont,
"close", "Close Application");
this.gui.attachChild(this.introCont);
@@ -356,46 +336,6 @@ public class CalibrateInputScreen extends BaseAppState
this.resize(camera.getWidth(), camera.getHeight());
}
private float alignContainer(Node cont, int width, int height)
{
float totalHeight = 0;
Vector2f prevButtonDimensions = null;
for (Spatial node : cont.getChildren())
{
if (node instanceof BitmapText)
{
// ((BitmapText) node).setAlignment(BitmapFont.Align.Center);
// ((BitmapText) node).setVerticalAlignment(BitmapFont.VAlign
// .Center);
node.setLocalTranslation(
-((BitmapText) node).getLineWidth() / 2F, -totalHeight,
0);
totalHeight += ((BitmapText) node).getHeight();
prevButtonDimensions = null;
}
else if (node.getUserData("button") != null)
{
Quad quad = ((Quad) ((Geometry) ((Node) node).getChild(0))
.getMesh());
if (prevButtonDimensions != null)
{
node.setLocalTranslation(
-quad.getWidth() / 2F + prevButtonDimensions.x + 10,
-totalHeight + prevButtonDimensions.y, 0);
}
else
{
node.setLocalTranslation(-quad.getWidth() / 2F,
-totalHeight - 10, 0);
totalHeight += quad.getHeight() + 10;
}
// prevButtonDimensions = new Vector2f(quad.getWidth(), quad
// .getHeight());
}
}
return totalHeight;
}
/**
* Scales and positions elements of this screen.
* @param width - The width to scale to.
@@ -403,8 +343,8 @@ public class CalibrateInputScreen extends BaseAppState
*/
protected void resize(int width, int height)
{
float introHeight = this.alignContainer(this.introCont, width, height);
float mainHeight = this.alignContainer(this.mainOptions, width, height);
float introHeight = GUIUtils.alignContainer(this.introCont, width, height);
float mainHeight = GUIUtils.alignContainer(this.mainOptions, width, height);
this.gui.setLocalTranslation(0, height / 2F, 0);
this.introCont.setLocalTranslation((width) / 2F, (introHeight) / 2F, 0);
this.mainOptions
@@ -433,22 +373,13 @@ public class CalibrateInputScreen extends BaseAppState
@Override
public void onAction(String name, boolean isPressed, float tpf)
{
CollisionResults results;
Ray ray;
Vector2f cursor;
String buttonId;
if (name.equals(CLICK_MAPPING) && !isPressed)
if (name.equals(CLICK_MAPPING))
{
cursor =
this.getApplication().getInputManager().getCursorPosition();
results = new CollisionResults();
ray = new Ray(new Vector3f(cursor.x, cursor.y, 0),
new Vector3f(cursor.x, cursor.y, 1));
this.gui.collideWith(ray, results);
for (CollisionResult result : results)
{
buttonId = result.getGeometry().getUserData("button");
if (buttonId != null)
buttonId = GUIUtils.handleButtonPress(this.gui,
this.getApplication().getInputManager().getCursorPosition(),
isPressed);
if (!isPressed && buttonId != null)
{
switch (buttonId)
{
@@ -490,7 +421,6 @@ public class CalibrateInputScreen extends BaseAppState
}
}
}
}
/**
* Set which joystick to use for calibration. This change affects all
@@ -501,8 +431,11 @@ public class CalibrateInputScreen extends BaseAppState
private void setJoystick(Joystick joystick)
{
this.joystick = joystick;
this.currentJoystick = this.guiFont.createLabel(this.joystick.getName());
this.currentJoystick.setBox(new Rectangle(0, 0, this.currentJoystick.getLineWidth(), this.currentJoystick.getHeight()));
this.currentJoystick =
this.guiFont.createLabel(this.joystick.getName());
this.currentJoystick
.setBox(new Rectangle(0, 0, this.currentJoystick.getLineWidth(),
this.currentJoystick.getHeight()));
this.currentJoystick.setAlignment(BitmapFont.Align.Center);
this.gui.attachChild(this.currentJoystick);
this.calibrationIter = BUTTON_PROMPTS.keySet().iterator();
@@ -700,6 +633,9 @@ public class CalibrateInputScreen extends BaseAppState
*/
private void recordFile()
{
/*
* Enable this in JME 3.4.
*/
boolean perComponentEnabled = false;
JoystickAxis axis;
JoystickButton button;
@@ -715,17 +651,17 @@ public class CalibrateInputScreen extends BaseAppState
{
if (!CALIBRATION_FILE.createNewFile())
{
throw new IOException(
"Could not create calibration file.");
throw new IOException("Could not create calibration file.");
}
}
catch (IOException ioe)
{
this.introCont.detachAllChildren();
this.introCont.attachChild(
this.guiFont.createLabel("Could not create Calibration File:"));
this.introCont.attachChild(this.guiFont
.createLabel("Could not create Calibration File:"));
this.introCont.attachChild(this.cancelButton);
this.introCont.attachChild(this.guiFont.createLabel("Error Details:"));
this.introCont.attachChild(
this.guiFont.createLabel("Error Details:"));
this.listStack(this.introCont, ioe, 0);
this.gui.attachChild(this.introCont);
this.resize();
@@ -771,11 +707,12 @@ public class CalibrateInputScreen extends BaseAppState
{
props.store(output, "Joystick Calibration File");
this.introCont.detachAllChildren();
this.introCont.attachChild(this.guiFont
.createLabel("Calibration completed successfully."));
this.introCont.attachChild(this.guiFont.createLabel(
"Close the application and restart to load"));
this.introCont.attachChild(
this.guiFont.createLabel("Calibration completed successfully."));
this.introCont.attachChild(
this.guiFont.createLabel("Close the application and restart to load"));
this.introCont.attachChild(this.guiFont.createLabel("the new settings."));
this.guiFont.createLabel("the new settings."));
this.introCont.attachChild(this.restartButton);
this.gui.attachChild(this.introCont);
this.resize();
@@ -783,10 +720,11 @@ public class CalibrateInputScreen extends BaseAppState
catch (IOException ioe)
{
this.introCont.detachAllChildren();
this.introCont.attachChild(
this.guiFont.createLabel("Could not create Calibration File:"));
this.introCont.attachChild(this.guiFont
.createLabel("Could not create Calibration File:"));
this.introCont.attachChild(this.cancelButton);
this.introCont.attachChild(this.guiFont.createLabel("Error Details:"));
this.introCont.attachChild(
this.guiFont.createLabel("Error Details:"));
this.listStack(this.introCont, ioe, 0);
this.gui.attachChild(this.introCont);
this.resize();
@@ -820,7 +758,8 @@ public class CalibrateInputScreen extends BaseAppState
errorBuilder.append('\n');
cause = error.getCause();
}
errorCont.attachChild(this.guiFont.createLabel(errorBuilder.toString()));
errorCont
.attachChild(this.guiFont.createLabel(errorBuilder.toString()));
}
@Override