diff --git a/SPD-classes/src/main/java/com/watabou/glscripts/Script.java b/SPD-classes/src/main/java/com/watabou/glscripts/Script.java index c59866cb9..899a009b0 100644 --- a/SPD-classes/src/main/java/com/watabou/glscripts/Script.java +++ b/SPD-classes/src/main/java/com/watabou/glscripts/Script.java @@ -37,7 +37,7 @@ public class Script extends Program { @SuppressWarnings("unchecked") public synchronized static T use( Class 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() { - } + } diff --git a/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java b/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java new file mode 100644 index 000000000..a401303e5 --- /dev/null +++ b/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java @@ -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(); + 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(); + } + } +} diff --git a/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java b/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java index d8afd4519..1753ba722 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java +++ b/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java @@ -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 diff --git a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java index 1ddf773f2..35ff10492 100644 --- a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java +++ b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java @@ -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 diff --git a/core/src/main/assets/gdx/textfield.atlas b/core/src/main/assets/gdx/textfield.atlas new file mode 100644 index 000000000..5a157c718 --- /dev/null +++ b/core/src/main/assets/gdx/textfield.atlas @@ -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 \ No newline at end of file diff --git a/core/src/main/assets/gdx/textfield.json b/core/src/main/assets/gdx/textfield.json new file mode 100644 index 000000000..e7d8c6ca1 --- /dev/null +++ b/core/src/main/assets/gdx/textfield.json @@ -0,0 +1,8 @@ +{ +Color: { + black: { a: 1, b: 0, g: 0, r: 0 }, +}, +TextFieldStyle: { + default: { selection: selection, fontColor: black, cursor: cursor } +} +} diff --git a/core/src/main/assets/gdx/textfield.png b/core/src/main/assets/gdx/textfield.png new file mode 100644 index 000000000..d3ad630ac Binary files /dev/null and b/core/src/main/assets/gdx/textfield.png differ diff --git a/core/src/main/assets/interfaces/chrome.png b/core/src/main/assets/interfaces/chrome.png index 46e12af36..c5f779d8d 100644 Binary files a/core/src/main/assets/interfaces/chrome.png and b/core/src/main/assets/interfaces/chrome.png differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Chrome.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Chrome.java index 5bd9e8820..8d065c4bf 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Chrome.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Chrome.java @@ -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: diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/WndTextInput.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/WndTextInput.java new file mode 100644 index 000000000..9b1a0392b --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/WndTextInput.java @@ -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 + } +} diff --git a/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java b/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java index c3dc73ba2..0da144921 100644 --- a/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java +++ b/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java @@ -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 diff --git a/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java b/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java index dde2be7b2..7283aa1bc 100644 --- a/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java +++ b/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java @@ -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);