diff --git a/SPD-classes/src/main/java/com/watabou/input/InputHandler.java b/SPD-classes/src/main/java/com/watabou/input/InputHandler.java index bb30580a3..88020a246 100644 --- a/SPD-classes/src/main/java/com/watabou/input/InputHandler.java +++ b/SPD-classes/src/main/java/com/watabou/input/InputHandler.java @@ -93,7 +93,6 @@ public class InputHandler extends InputAdapter { public synchronized boolean touchDown(int screenX, int screenY, int pointer, int button) { ControllerHandler.setControllerPointer(false); ControllerHandler.controllerActive = false; - Gdx.input.setOnscreenKeyboardVisible(false); //in-game events never need keyboard, so hide it if (button >= 3 && KeyBindings.isKeyBound( button + 1000 )) { KeyEvent.addKeyEvent( new KeyEvent( button + 1000, true ) ); diff --git a/SPD-classes/src/main/java/com/watabou/input/PointerEvent.java b/SPD-classes/src/main/java/com/watabou/input/PointerEvent.java index 43f47af63..4e9352cd2 100644 --- a/SPD-classes/src/main/java/com/watabou/input/PointerEvent.java +++ b/SPD-classes/src/main/java/com/watabou/input/PointerEvent.java @@ -21,6 +21,7 @@ package com.watabou.input; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.watabou.noosa.Game; import com.watabou.noosa.ui.Cursor; @@ -138,6 +139,8 @@ public class PointerEvent { pointerEvents.add(event); } } + + public static boolean clearKeyboardThisPress = true; public static synchronized void processPointerEvents(){ //handle any hover events separately first as we may need to add drag events @@ -164,6 +167,7 @@ public class PointerEvent { if (p.type == Type.HOVER){ continue; } + clearKeyboardThisPress = true; if (activePointers.containsKey(p.id)){ PointerEvent existing = activePointers.get(p.id); existing.current = p.current; @@ -181,6 +185,10 @@ public class PointerEvent { } pointerSignal.dispatch(p); } + if (clearKeyboardThisPress){ + //most press events should clear the keyboard + Gdx.input.setOnscreenKeyboardVisible(false); + } } pointerEvents.clear(); } diff --git a/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java b/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java index 84869de7c..b997c6a71 100644 --- a/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java +++ b/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java @@ -126,6 +126,31 @@ public class TextInput extends Component { return textField.getText(); } + public void copyToClipboard(){ + if (textField.getSelection().isEmpty()) { + textField.selectAll(); + } + + textField.copy(); + } + + public void pasteFromClipboard(){ + if (!Gdx.app.getClipboard().hasContents()) return; + + if (!textField.getSelection().isEmpty()){ + //just use cut, but override clipboard + String existingClip = Gdx.app.getClipboard().getContents(); + textField.cut(); + Gdx.app.getClipboard().setContents(existingClip); + } + + String existing = textField.getText(); + int cursorIdx = textField.getCursorPosition(); + + textField.setText(existing.substring(0, cursorIdx) + Gdx.app.getClipboard().getContents() + existing.substring(cursorIdx)); + textField.setCursorPosition(cursorIdx + Gdx.app.getClipboard().getContents().length()); + } + @Override protected void layout() { super.layout(); diff --git a/core/src/main/assets/interfaces/icons.png b/core/src/main/assets/interfaces/icons.png index 2ffc63cf9..3c96d8f9c 100644 Binary files a/core/src/main/assets/interfaces/icons.png and b/core/src/main/assets/interfaces/icons.png differ diff --git a/core/src/main/assets/messages/scenes/scenes.properties b/core/src/main/assets/messages/scenes/scenes.properties index c2ba3399b..04ce74eea 100644 --- a/core/src/main/assets/messages/scenes/scenes.properties +++ b/core/src/main/assets/messages/scenes/scenes.properties @@ -60,7 +60,7 @@ scenes.heroselectscene.daily_unavailable=A new daily run is available every day scenes.heroselectscene.daily_unavailable_long=It seems you've started a daily that's in the future! This can happen if you recently changed timezones, or if you changed your system clock. _Your next daily will be available in %d days._ scenes.heroselectscene.daily_existing=You already have a daily run in progress. You must end that run before starting another daily. scenes.heroselectscene.custom_seed_title=Enter a Custom Seed -scenes.heroselectscene.custom_seed_desc=The same seed and game version always generate the same dungeon! Seeds are nine uppercase letters (e.g. ABC-DEF-GHI), but you can enter anything you want and the game will convert it. _Games with a custom seed cannot earn badges, contribute to games played, and appear at the bottom of the rankings page._ +scenes.heroselectscene.custom_seed_desc=The same seed and game version always generate the same dungeon! _Custom seed games cannot earn badges or contribute to games played, and appear at the bottom of the rankings page._ scenes.heroselectscene.custom_seed_duplicate=You already have a regular game in progress with that seed. You must end that game before using that custom seed. scenes.heroselectscene.custom_seed_set=Set scenes.heroselectscene.custom_seed_clear=Clear diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java index 48eb5ca03..58e86d566 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java @@ -80,6 +80,8 @@ public enum Icons { MAGNIFY, BUFFS, ENERGY, + COPY, + PASTE, COIN_SML, ENERGY_SML, BACKPACK, @@ -259,26 +261,32 @@ public enum Icons { case ENERGY: icon.frame( icon.texture.uvRectBySize( 176, 48, 16, 16 ) ); break; + case COPY: + icon.frame( icon.texture.uvRectBySize( 192, 48, 13, 13 ) ); + break; + case PASTE: + icon.frame( icon.texture.uvRectBySize( 208, 48, 13, 13 ) ); + break; case COIN_SML: - icon.frame( icon.texture.uvRectBySize( 192, 48, 7, 7 ) ); + icon.frame( icon.texture.uvRectBySize( 192, 64, 7, 7 ) ); break; case ENERGY_SML: - icon.frame( icon.texture.uvRectBySize( 192, 56, 8, 7 ) ); + icon.frame( icon.texture.uvRectBySize( 192, 72, 8, 7 ) ); break; case BACKPACK: - icon.frame( icon.texture.uvRectBySize( 201, 48, 10, 10 ) ); + icon.frame( icon.texture.uvRectBySize( 201, 64, 10, 10 ) ); break; case SCROLL_HOLDER: - icon.frame( icon.texture.uvRectBySize( 211, 48, 10, 10 ) ); + icon.frame( icon.texture.uvRectBySize( 211, 64, 10, 10 ) ); break; case SEED_POUCH: - icon.frame( icon.texture.uvRectBySize( 221, 48, 10, 10 ) ); + icon.frame( icon.texture.uvRectBySize( 221, 64, 10, 10 ) ); break; case WAND_HOLSTER: - icon.frame( icon.texture.uvRectBySize( 231, 48, 10, 10 ) ); + icon.frame( icon.texture.uvRectBySize( 231, 64, 10, 10 ) ); break; case POTION_BANDOLIER: - icon.frame( icon.texture.uvRectBySize( 241, 48, 10, 10 ) ); + icon.frame( icon.texture.uvRectBySize( 241, 64, 10, 10 ) ); break; case TARGET: diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StyledButton.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StyledButton.java index eb9e6d41d..3f3eaecc2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StyledButton.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StyledButton.java @@ -113,6 +113,7 @@ public class StyledButton extends Button { public void enable( boolean value ) { active = value; text.alpha( value ? 1.0f : 0.3f ); + if (icon != null) icon.alpha( value ? 1.0f : 0.3f ); } public void text( String value ) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTextInput.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTextInput.java index 1fadeaa48..aaabd32d7 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTextInput.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTextInput.java @@ -21,23 +21,29 @@ package com.shatteredpixel.shatteredpixeldungeon.windows; +import com.badlogic.gdx.Gdx; import com.shatteredpixel.shatteredpixeldungeon.Chrome; import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; +import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; import com.shatteredpixel.shatteredpixeldungeon.ui.Window; +import com.watabou.input.PointerEvent; import com.watabou.noosa.TextInput; import com.watabou.utils.DeviceCompat; public class WndTextInput extends Window { - private static final int WIDTH = 130; - private static final int W_LAND_EXTRA = 200; //extra width is sometimes used in landscape - private static final int MARGIN = 2; + private static final int WIDTH = 135; + private static final int W_LAND_EXTRA = 220; //extra width is sometimes used in landscape + private static final int MARGIN = 1; private static final int BUTTON_HEIGHT = 16; protected TextInput textBox; + protected RedButton btnCopy; + protected RedButton btnPaste; + public WndTextInput(final String title, final String body, final String initialValue, final int maxLength, final boolean multiLine, final String posTxt, final String negTxt) { super(); @@ -67,7 +73,7 @@ public class WndTextInput extends Window { txtTitle.setPos((width - txtTitle.width()) / 2, 2); add(txtTitle); - pos = txtTitle.bottom() + 2 * MARGIN; + pos = txtTitle.bottom() + 4 * MARGIN; } if (body != null) { @@ -98,8 +104,59 @@ public class WndTextInput extends Window { } else { inputHeight = 16; } + + float textBoxWidth = width-3*MARGIN-BUTTON_HEIGHT; + add(textBox); - textBox.setRect(MARGIN, pos, width-2*MARGIN, inputHeight); + textBox.setRect(MARGIN, pos, textBoxWidth, inputHeight); + + btnCopy = new RedButton(""){ + @Override + protected void onPointerDown() { + super.onPointerDown(); + PointerEvent.clearKeyboardThisPress = false; + } + + @Override + protected void onPointerUp() { + super.onPointerUp(); + PointerEvent.clearKeyboardThisPress = false; + } + + @Override + protected void onClick() { + super.onClick(); + textBox.copyToClipboard(); + } + }; + btnCopy.icon(Icons.COPY.get()); + add(btnCopy); + + btnPaste = new RedButton(""){ + @Override + protected void onPointerDown() { + super.onPointerDown(); + PointerEvent.clearKeyboardThisPress = false; + } + + @Override + protected void onPointerUp() { + super.onPointerUp(); + PointerEvent.clearKeyboardThisPress = false; + } + + @Override + protected void onClick() { + super.onClick(); + textBox.pasteFromClipboard(); + } + + }; + btnPaste.icon(Icons.PASTE.get()); + add(btnPaste); + + btnCopy.setRect(textBoxWidth + 2*MARGIN, pos, BUTTON_HEIGHT, BUTTON_HEIGHT); + btnPaste.setRect(textBoxWidth + 2*MARGIN, btnCopy.bottom()+MARGIN, BUTTON_HEIGHT, BUTTON_HEIGHT); pos += inputHeight + MARGIN; @@ -124,23 +181,33 @@ public class WndTextInput extends Window { negativeBtn = null; } + float btnWidth = multiLine ? width-2*MARGIN : textBoxWidth; if (negTxt != null) { - positiveBtn.setRect(MARGIN, pos, (width - MARGIN * 3) / 2, BUTTON_HEIGHT); + positiveBtn.setRect(MARGIN, pos, (btnWidth - MARGIN) / 2, BUTTON_HEIGHT); add(positiveBtn); - negativeBtn.setRect(positiveBtn.right() + MARGIN, pos, (width - MARGIN * 3) / 2, BUTTON_HEIGHT); + negativeBtn.setRect(positiveBtn.right() + MARGIN, pos, (btnWidth - MARGIN) / 2, BUTTON_HEIGHT); add(negativeBtn); } else { - positiveBtn.setRect(MARGIN, pos, width - MARGIN * 2, BUTTON_HEIGHT); + positiveBtn.setRect(MARGIN, pos, btnWidth, BUTTON_HEIGHT); add(positiveBtn); } - pos += BUTTON_HEIGHT + MARGIN; + pos += BUTTON_HEIGHT; //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); + textBox.setRect(MARGIN, textBox.top(), textBoxWidth, inputHeight); + PointerEvent.clearKeyboardThisPress = false; + + } + + @Override + public synchronized void update() { + super.update(); + btnCopy.enable(!textBox.getText().isEmpty()); + btnPaste.enable(Gdx.app.getClipboard().hasContents()); } @Override