v0.9.4: overhauled text input, now multiplatform using libGDX textField
This commit is contained in:
parent
bcdbf8ef43
commit
7307c38a3c
|
@ -37,7 +37,7 @@ public class Script extends Program {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized static<T extends Script> T use( Class<T> c ) {
|
||||
|
||||
|
||||
if (c != curScriptClass) {
|
||||
|
||||
Script script = all.get( c );
|
||||
|
@ -45,11 +45,7 @@ public class Script extends Program {
|
|||
script = Reflection.newInstance( c );
|
||||
all.put( c, script );
|
||||
}
|
||||
|
||||
if (curScript != null) {
|
||||
curScript.unuse();
|
||||
}
|
||||
|
||||
|
||||
curScript = script;
|
||||
curScriptClass = c;
|
||||
curScript.use();
|
||||
|
@ -58,6 +54,11 @@ public class Script extends Program {
|
|||
|
||||
return (T)curScript;
|
||||
}
|
||||
|
||||
public synchronized static void unuse(){
|
||||
curScript = null;
|
||||
curScriptClass = null;
|
||||
}
|
||||
|
||||
public synchronized static void reset() {
|
||||
for (Script script:all.values()) {
|
||||
|
@ -77,7 +78,5 @@ public class Script extends Program {
|
|||
link();
|
||||
|
||||
}
|
||||
|
||||
public void unuse() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
149
SPD-classes/src/main/java/com/watabou/noosa/TextInput.java
Normal file
149
SPD-classes/src/main/java/com/watabou/noosa/TextInput.java
Normal file
|
@ -0,0 +1,149 @@
|
|||
package com.watabou.noosa;
|
||||
|
||||
import com.badlogic.gdx.Files;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.Stage;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Container;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextArea;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
||||
import com.watabou.glscripts.Script;
|
||||
import com.watabou.glwrap.Blending;
|
||||
import com.watabou.glwrap.Quad;
|
||||
import com.watabou.glwrap.Texture;
|
||||
import com.watabou.noosa.ui.Component;
|
||||
import com.watabou.utils.FileUtils;
|
||||
import com.watabou.utils.Point;
|
||||
|
||||
//essentially contains a libGDX text input field, plus a PD-rendered background
|
||||
public class TextInput extends Component {
|
||||
|
||||
private Stage stage;
|
||||
private Container container;
|
||||
private TextField textField;
|
||||
|
||||
private Skin skin;
|
||||
|
||||
private NinePatch bg;
|
||||
|
||||
public TextInput( NinePatch bg, boolean multiline ){
|
||||
super();
|
||||
this.bg = bg;
|
||||
add(bg);
|
||||
|
||||
stage = new Stage(new ScreenViewport());
|
||||
Game.inputHandler.addInputProcessor(stage);
|
||||
|
||||
container = new Container<TextField>();
|
||||
stage.addActor(container);
|
||||
container.setTransform(true);
|
||||
|
||||
skin = new Skin(FileUtils.getFileHandle(Files.FileType.Internal, "gdx/textfield.json"));
|
||||
|
||||
int zoom = (int) Camera.main.zoom;
|
||||
int textSize = multiline ? 6 : 9;
|
||||
TextField.TextFieldStyle style = skin.get(TextField.TextFieldStyle.class);
|
||||
style.font = Game.platform.getFont(textSize*zoom, "", false, false);
|
||||
style.background = null;
|
||||
textField = multiline ? new TextArea("", style) : new TextField("", style);
|
||||
textField.setProgrammaticChangeEvents(true);
|
||||
|
||||
if (!multiline) textField.setAlignment(Align.center);
|
||||
|
||||
textField.addListener(new ChangeListener() {
|
||||
@Override
|
||||
public void changed(ChangeEvent event, Actor actor) {
|
||||
BitmapFont f = Game.platform.getFont(textSize*zoom, textField.getText(), false, false);
|
||||
TextField.TextFieldStyle style = textField.getStyle();
|
||||
if (f != style.font){
|
||||
style.font = f;
|
||||
textField.setStyle(style);
|
||||
}
|
||||
}
|
||||
});
|
||||
container.setActor(textField);
|
||||
stage.setKeyboardFocus(textField);
|
||||
Gdx.input.setOnscreenKeyboardVisible(true);
|
||||
}
|
||||
|
||||
public void setText(String text){
|
||||
textField.setText(text);
|
||||
textField.setCursorPosition(textField.getText().length());
|
||||
}
|
||||
|
||||
public void setMaxLength(int maxLength){
|
||||
textField.setMaxLength(maxLength);
|
||||
}
|
||||
|
||||
public String getText(){
|
||||
return textField.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layout() {
|
||||
super.layout();
|
||||
|
||||
float contX = x;
|
||||
float contY = y;
|
||||
float contW = width;
|
||||
float contH = height;
|
||||
|
||||
if (bg != null){
|
||||
bg.x = x;
|
||||
bg.y = y;
|
||||
bg.size(width, height);
|
||||
|
||||
contX += bg.marginLeft();
|
||||
contY += bg.marginTop();
|
||||
contW -= bg.marginHor();
|
||||
contH -= bg.marginVer();
|
||||
}
|
||||
|
||||
float zoom = Camera.main.zoom;
|
||||
Camera c = camera();
|
||||
if (c != null){
|
||||
zoom = c.zoom;
|
||||
Point p = c.cameraToScreen(contX, contY);
|
||||
contX = p.x/zoom;
|
||||
contY = p.y/zoom;
|
||||
}
|
||||
|
||||
container.align(Align.topLeft);
|
||||
container.setPosition(contX*zoom, (Game.height-(contY*zoom)));
|
||||
container.size(contW*zoom, contH*zoom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
stage.act(Game.elapsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
super.draw();
|
||||
Quad.releaseIndices();
|
||||
Script.unuse();
|
||||
Texture.clear();
|
||||
stage.draw();
|
||||
Quad.bindIndices();
|
||||
Blending.useDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void destroy() {
|
||||
super.destroy();
|
||||
if (stage != null) {
|
||||
stage.dispose();
|
||||
skin.dispose();
|
||||
Game.inputHandler.removeInputProcessor(stage);
|
||||
Gdx.input.setOnscreenKeyboardVisible(false);
|
||||
Game.platform.updateSystemUI();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,15 +37,6 @@ public abstract class PlatformSupport {
|
|||
public abstract void updateSystemUI();
|
||||
|
||||
public abstract boolean connectedToUnmeteredNetwork();
|
||||
|
||||
//FIXME this is currently used because no platform-agnostic text input has been implemented.
|
||||
//should look into doing that using either plain openGL or libgdx's libraries
|
||||
public abstract void promptTextInput( String title, String hintText, int maxLen, boolean multiLine,
|
||||
String posTxt, String negTxt, TextCallback callback);
|
||||
|
||||
public static abstract class TextCallback {
|
||||
public abstract void onSelect( boolean positive, String text );
|
||||
}
|
||||
|
||||
public void vibrate( int millis ){
|
||||
//regular GDX vibration by default
|
||||
|
|
|
@ -160,22 +160,6 @@ public class AndroidPlatformSupport extends PlatformSupport {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void promptTextInput(final String title, final String hintText, final int maxLen, final boolean multiLine, final String posTxt, final String negTxt, final TextCallback callback) {
|
||||
Game.runOnRenderThread( new Callback() {
|
||||
@Override
|
||||
public void call() {
|
||||
Game.scene().addToFront(new WndAndroidTextInput(title, hintText, maxLen, multiLine, posTxt, negTxt) {
|
||||
@Override
|
||||
protected void onSelect(boolean positive) {
|
||||
callback.onSelect(positive, getText());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/* FONT SUPPORT */
|
||||
|
||||
//droid sans / roboto, or a custom pixel font, for use with Latin and Cyrillic languages
|
||||
|
|
21
core/src/main/assets/gdx/textfield.atlas
Normal file
21
core/src/main/assets/gdx/textfield.atlas
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
textfield.png
|
||||
size: 4,4
|
||||
format: RGBA8888
|
||||
filter: Nearest,Nearest
|
||||
repeat: none
|
||||
cursor
|
||||
rotate: false
|
||||
xy: 0, 0
|
||||
size: 3, 3
|
||||
split: 1, 1, 1, 1
|
||||
orig: 3, 3
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
selection
|
||||
rotate: false
|
||||
xy: 3, 3
|
||||
size: 1, 1
|
||||
orig: 1, 1
|
||||
offset: 0, 0
|
||||
index: -1
|
8
core/src/main/assets/gdx/textfield.json
Normal file
8
core/src/main/assets/gdx/textfield.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
Color: {
|
||||
black: { a: 1, b: 0, g: 0, r: 0 },
|
||||
},
|
||||
TextFieldStyle: {
|
||||
default: { selection: selection, fontColor: black, cursor: cursor }
|
||||
}
|
||||
}
|
BIN
core/src/main/assets/gdx/textfield.png
Normal file
BIN
core/src/main/assets/gdx/textfield.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 81 B |
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -28,6 +28,7 @@ public class Chrome {
|
|||
public enum Type {
|
||||
TOAST,
|
||||
TOAST_TR,
|
||||
TOAST_WHITE,
|
||||
WINDOW,
|
||||
WINDOW_SILVER,
|
||||
RED_BUTTON,
|
||||
|
@ -53,10 +54,12 @@ public class Chrome {
|
|||
case TOAST_TR:
|
||||
case GREY_BUTTON_TR:
|
||||
return new NinePatch( Asset, 20, 9, 9, 9, 4 );
|
||||
case TOAST_WHITE:
|
||||
return new NinePatch( Asset, 29, 0, 9, 9, 4 );
|
||||
case RED_BUTTON:
|
||||
return new NinePatch( Asset, 29, 0, 6, 6, 2 );
|
||||
return new NinePatch( Asset, 38, 0, 6, 6, 2 );
|
||||
case GREY_BUTTON:
|
||||
return new NinePatch( Asset, 29, 6, 6, 6, 2 );
|
||||
return new NinePatch( Asset, 38, 6, 6, 6, 2 );
|
||||
case TAG:
|
||||
return new NinePatch( Asset, 22, 18, 16, 14, 3 );
|
||||
case GEM:
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package com.shatteredpixel.shatteredpixeldungeon.ui;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Chrome;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
|
||||
import com.watabou.noosa.TextInput;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
|
||||
public class WndTextInput extends Window {
|
||||
|
||||
private static final int WIDTH = 120;
|
||||
private static final int W_LAND_MULTI = 200; //in the specific case of multiline in landscape
|
||||
private static final int MARGIN = 2;
|
||||
private static final int BUTTON_HEIGHT = 16;
|
||||
|
||||
public WndTextInput(final String title, final String initialValue, final int maxLength,
|
||||
final boolean multiLine, final String posTxt, final String negTxt) {
|
||||
super();
|
||||
|
||||
//need to offset to give space for the soft keyboard
|
||||
if (!DeviceCompat.isDesktop()) {
|
||||
if (PixelScene.landscape()) {
|
||||
offset(-45);
|
||||
} else {
|
||||
offset(multiLine ? -60 : -45);
|
||||
}
|
||||
}
|
||||
|
||||
final int width;
|
||||
if (PixelScene.landscape() && multiLine) {
|
||||
width = W_LAND_MULTI; //more editing space for landscape users
|
||||
} else {
|
||||
width = WIDTH;
|
||||
}
|
||||
|
||||
final RenderedTextBlock txtTitle = PixelScene.renderTextBlock(title, 9);
|
||||
txtTitle.maxWidth(width);
|
||||
txtTitle.hardlight(Window.TITLE_COLOR);
|
||||
txtTitle.setPos((width - txtTitle.width()) / 2, 2);
|
||||
add(txtTitle);
|
||||
|
||||
TextInput textBox = new TextInput(Chrome.get(Chrome.Type.TOAST_WHITE), multiLine);
|
||||
if (initialValue != null) textBox.setText(initialValue);
|
||||
textBox.setMaxLength(maxLength);
|
||||
|
||||
float pos = txtTitle.bottom() + 2 * MARGIN;
|
||||
|
||||
//sets different height depending on whether this is a single or multi line input.
|
||||
final float inputHeight;
|
||||
if (multiLine) {
|
||||
inputHeight = 64; //~8 lines of text
|
||||
} else {
|
||||
inputHeight = 16;
|
||||
}
|
||||
add(textBox);
|
||||
textBox.setRect(MARGIN, pos, width-2*MARGIN, inputHeight);
|
||||
|
||||
pos += inputHeight + MARGIN;
|
||||
|
||||
final RedButton positiveBtn = new RedButton(posTxt) {
|
||||
@Override
|
||||
protected void onClick() {
|
||||
onSelect(true, textBox.getText());
|
||||
hide();
|
||||
}
|
||||
};
|
||||
|
||||
final RedButton negativeBtn;
|
||||
if (negTxt != null) {
|
||||
negativeBtn = new RedButton(negTxt) {
|
||||
@Override
|
||||
protected void onClick() {
|
||||
onSelect(false, textBox.getText());
|
||||
hide();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
negativeBtn = null;
|
||||
}
|
||||
|
||||
if (negTxt != null) {
|
||||
positiveBtn.setRect(MARGIN, pos, (width - MARGIN * 3) / 2, BUTTON_HEIGHT);
|
||||
add(positiveBtn);
|
||||
negativeBtn.setRect(positiveBtn.right() + MARGIN, pos, (width - MARGIN * 3) / 2, BUTTON_HEIGHT);
|
||||
add(negativeBtn);
|
||||
} else {
|
||||
positiveBtn.setRect(MARGIN, pos, width - MARGIN * 2, BUTTON_HEIGHT);
|
||||
add(positiveBtn);
|
||||
}
|
||||
|
||||
pos += BUTTON_HEIGHT + MARGIN;
|
||||
|
||||
//need to resize first before laying out the text box, as it depends on the window's camera
|
||||
resize(width, (int) pos);
|
||||
|
||||
textBox.setRect(MARGIN, textBox.top(), width-2*MARGIN, inputHeight);
|
||||
|
||||
}
|
||||
|
||||
public void onSelect(boolean positive, String text){ }
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
//Do nothing, prevents accidentally losing writing
|
||||
}
|
||||
}
|
|
@ -66,21 +66,6 @@ public class DesktopPlatformSupport extends PlatformSupport {
|
|||
public boolean connectedToUnmeteredNetwork() {
|
||||
return true; //no easy way to check this in desktop, just assume user doesn't care
|
||||
}
|
||||
|
||||
@Override
|
||||
//FIXME tinyfd_inputBox isn't a full solution for this. No support for multiline, looks ugly. Ideally we'd have an opengl-based input box
|
||||
public void promptTextInput(String title, String hintText, int maxLen, boolean multiLine, String posTxt, String negTxt, TextCallback callback) {
|
||||
String result = TinyFileDialogs.tinyfd_inputBox(title, title, hintText);
|
||||
if (result == null){
|
||||
callback.onSelect(false, "");
|
||||
} else {
|
||||
if (result.contains("\r\n")) result = result.substring(0, result.indexOf("\r\n"));
|
||||
if (result.contains("\n")) result = result.substring(0, result.indexOf("\n"));
|
||||
if (result.length() > maxLen) result = result.substring(0, maxLen);
|
||||
callback.onSelect(true, result.replace("\r\n", "").replace("\n", ""));
|
||||
}
|
||||
}
|
||||
|
||||
/* FONT SUPPORT */
|
||||
|
||||
//custom pixel font, for use with Latin and Cyrillic languages
|
||||
|
|
|
@ -56,11 +56,6 @@ public class IOSPlatformSupport extends PlatformSupport {
|
|||
return !test.getFlags().contains(SCNetworkReachabilityFlags.IsWWAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void promptTextInput(String title, String hintText, int maxLen, boolean multiLine, String posTxt, String negTxt, TextCallback callback) {
|
||||
//TODO need multiplat text input, this does nothing atm!
|
||||
}
|
||||
|
||||
public void vibrate( int millis ){
|
||||
//gives a short vibrate on iPhone 6+, no vibration otherwise
|
||||
AudioServices.playSystemSound(1520);
|
||||
|
|
Loading…
Reference in New Issue
Block a user