v1.4.0: implemented a copy and paste button for text input

This commit is contained in:
Evan Debenham
2022-08-11 16:56:49 -04:00
parent 7646489bc0
commit dcc84e4e7e
8 changed files with 127 additions and 19 deletions

View File

@@ -93,7 +93,6 @@ public class InputHandler extends InputAdapter {
public synchronized boolean touchDown(int screenX, int screenY, int pointer, int button) { public synchronized boolean touchDown(int screenX, int screenY, int pointer, int button) {
ControllerHandler.setControllerPointer(false); ControllerHandler.setControllerPointer(false);
ControllerHandler.controllerActive = false; ControllerHandler.controllerActive = false;
Gdx.input.setOnscreenKeyboardVisible(false); //in-game events never need keyboard, so hide it
if (button >= 3 && KeyBindings.isKeyBound( button + 1000 )) { if (button >= 3 && KeyBindings.isKeyBound( button + 1000 )) {
KeyEvent.addKeyEvent( new KeyEvent( button + 1000, true ) ); KeyEvent.addKeyEvent( new KeyEvent( button + 1000, true ) );

View File

@@ -21,6 +21,7 @@
package com.watabou.input; package com.watabou.input;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
import com.watabou.noosa.Game; import com.watabou.noosa.Game;
import com.watabou.noosa.ui.Cursor; import com.watabou.noosa.ui.Cursor;
@@ -139,6 +140,8 @@ public class PointerEvent {
} }
} }
public static boolean clearKeyboardThisPress = true;
public static synchronized void processPointerEvents(){ public static synchronized void processPointerEvents(){
//handle any hover events separately first as we may need to add drag events //handle any hover events separately first as we may need to add drag events
boolean hovered = false; boolean hovered = false;
@@ -164,6 +167,7 @@ public class PointerEvent {
if (p.type == Type.HOVER){ if (p.type == Type.HOVER){
continue; continue;
} }
clearKeyboardThisPress = true;
if (activePointers.containsKey(p.id)){ if (activePointers.containsKey(p.id)){
PointerEvent existing = activePointers.get(p.id); PointerEvent existing = activePointers.get(p.id);
existing.current = p.current; existing.current = p.current;
@@ -181,6 +185,10 @@ public class PointerEvent {
} }
pointerSignal.dispatch(p); pointerSignal.dispatch(p);
} }
if (clearKeyboardThisPress){
//most press events should clear the keyboard
Gdx.input.setOnscreenKeyboardVisible(false);
}
} }
pointerEvents.clear(); pointerEvents.clear();
} }

View File

@@ -126,6 +126,31 @@ public class TextInput extends Component {
return textField.getText(); 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 @Override
protected void layout() { protected void layout() {
super.layout(); super.layout();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -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_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.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_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_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_set=Set
scenes.heroselectscene.custom_seed_clear=Clear scenes.heroselectscene.custom_seed_clear=Clear

View File

@@ -80,6 +80,8 @@ public enum Icons {
MAGNIFY, MAGNIFY,
BUFFS, BUFFS,
ENERGY, ENERGY,
COPY,
PASTE,
COIN_SML, COIN_SML,
ENERGY_SML, ENERGY_SML,
BACKPACK, BACKPACK,
@@ -259,26 +261,32 @@ public enum Icons {
case ENERGY: case ENERGY:
icon.frame( icon.texture.uvRectBySize( 176, 48, 16, 16 ) ); icon.frame( icon.texture.uvRectBySize( 176, 48, 16, 16 ) );
break; 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: case COIN_SML:
icon.frame( icon.texture.uvRectBySize( 192, 48, 7, 7 ) ); icon.frame( icon.texture.uvRectBySize( 192, 64, 7, 7 ) );
break; break;
case ENERGY_SML: case ENERGY_SML:
icon.frame( icon.texture.uvRectBySize( 192, 56, 8, 7 ) ); icon.frame( icon.texture.uvRectBySize( 192, 72, 8, 7 ) );
break; break;
case BACKPACK: case BACKPACK:
icon.frame( icon.texture.uvRectBySize( 201, 48, 10, 10 ) ); icon.frame( icon.texture.uvRectBySize( 201, 64, 10, 10 ) );
break; break;
case SCROLL_HOLDER: case SCROLL_HOLDER:
icon.frame( icon.texture.uvRectBySize( 211, 48, 10, 10 ) ); icon.frame( icon.texture.uvRectBySize( 211, 64, 10, 10 ) );
break; break;
case SEED_POUCH: case SEED_POUCH:
icon.frame( icon.texture.uvRectBySize( 221, 48, 10, 10 ) ); icon.frame( icon.texture.uvRectBySize( 221, 64, 10, 10 ) );
break; break;
case WAND_HOLSTER: case WAND_HOLSTER:
icon.frame( icon.texture.uvRectBySize( 231, 48, 10, 10 ) ); icon.frame( icon.texture.uvRectBySize( 231, 64, 10, 10 ) );
break; break;
case POTION_BANDOLIER: case POTION_BANDOLIER:
icon.frame( icon.texture.uvRectBySize( 241, 48, 10, 10 ) ); icon.frame( icon.texture.uvRectBySize( 241, 64, 10, 10 ) );
break; break;
case TARGET: case TARGET:

View File

@@ -113,6 +113,7 @@ public class StyledButton extends Button {
public void enable( boolean value ) { public void enable( boolean value ) {
active = value; active = value;
text.alpha( value ? 1.0f : 0.3f ); text.alpha( value ? 1.0f : 0.3f );
if (icon != null) icon.alpha( value ? 1.0f : 0.3f );
} }
public void text( String value ) { public void text( String value ) {

View File

@@ -21,23 +21,29 @@
package com.shatteredpixel.shatteredpixeldungeon.windows; package com.shatteredpixel.shatteredpixeldungeon.windows;
import com.badlogic.gdx.Gdx;
import com.shatteredpixel.shatteredpixeldungeon.Chrome; import com.shatteredpixel.shatteredpixeldungeon.Chrome;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton; import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window; import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.watabou.input.PointerEvent;
import com.watabou.noosa.TextInput; import com.watabou.noosa.TextInput;
import com.watabou.utils.DeviceCompat; import com.watabou.utils.DeviceCompat;
public class WndTextInput extends Window { public class WndTextInput extends Window {
private static final int WIDTH = 130; private static final int WIDTH = 135;
private static final int W_LAND_EXTRA = 200; //extra width is sometimes used in landscape private static final int W_LAND_EXTRA = 220; //extra width is sometimes used in landscape
private static final int MARGIN = 2; private static final int MARGIN = 1;
private static final int BUTTON_HEIGHT = 16; private static final int BUTTON_HEIGHT = 16;
protected TextInput textBox; protected TextInput textBox;
protected RedButton btnCopy;
protected RedButton btnPaste;
public WndTextInput(final String title, final String body, final String initialValue, final int maxLength, public WndTextInput(final String title, final String body, final String initialValue, final int maxLength,
final boolean multiLine, final String posTxt, final String negTxt) { final boolean multiLine, final String posTxt, final String negTxt) {
super(); super();
@@ -67,7 +73,7 @@ public class WndTextInput extends Window {
txtTitle.setPos((width - txtTitle.width()) / 2, 2); txtTitle.setPos((width - txtTitle.width()) / 2, 2);
add(txtTitle); add(txtTitle);
pos = txtTitle.bottom() + 2 * MARGIN; pos = txtTitle.bottom() + 4 * MARGIN;
} }
if (body != null) { if (body != null) {
@@ -98,8 +104,59 @@ public class WndTextInput extends Window {
} else { } else {
inputHeight = 16; inputHeight = 16;
} }
float textBoxWidth = width-3*MARGIN-BUTTON_HEIGHT;
add(textBox); 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; pos += inputHeight + MARGIN;
@@ -124,23 +181,33 @@ public class WndTextInput extends Window {
negativeBtn = null; negativeBtn = null;
} }
float btnWidth = multiLine ? width-2*MARGIN : textBoxWidth;
if (negTxt != null) { if (negTxt != null) {
positiveBtn.setRect(MARGIN, pos, (width - MARGIN * 3) / 2, BUTTON_HEIGHT); positiveBtn.setRect(MARGIN, pos, (btnWidth - MARGIN) / 2, BUTTON_HEIGHT);
add(positiveBtn); 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); add(negativeBtn);
} else { } else {
positiveBtn.setRect(MARGIN, pos, width - MARGIN * 2, BUTTON_HEIGHT); positiveBtn.setRect(MARGIN, pos, btnWidth, BUTTON_HEIGHT);
add(positiveBtn); 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 //need to resize first before laying out the text box, as it depends on the window's camera
resize(width, (int) pos); 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 @Override