I set up a stage with three TextFields in my libgdx app, and I get different behaviour in the desktop mode and the Android mode. On Android, typing the enter key moves the cursor to the next TextField. On the desktop, typing the enter key does nothing.
How can I make the cursor move consistently on both platforms? I want to be able to set the focus to another field when the user types enter. On Android, whatever I set the focus to, the default enter key behaviour then jumps the focus to the field after that.
Here's the code I'm currently using to move the cursor and clear the next field:
stage.addListener(new InputListener() {
#Override
public boolean keyUp(InputEvent event, int keycode) {
if (keycode == Input.Keys.ENTER) {
nextField();
}
return false;
}
});
Gdx.input.setInputProcessor(stage);
}
private void nextField() {
TextField nextField =
stage.getKeyboardFocus() == text1
? text2
: stage.getKeyboardFocus() == text2
? text3
: text1;
nextField.setText("");
stage.setKeyboardFocus(nextField);
}
I've tried cancelling the event or returning true from the handler methods, but the focus still moves after my code finishes.
My complete sample code is on GitHub.
TextField uses a private internal InputListener, that gets initialized in the constructor and cannot be easily overwritten. The relevant code that changes the focus is during the keyTyped method of this listener:
public boolean keyTyped (InputEvent event, char character) {
[...]
if ((character == TAB || character == ENTER_ANDROID) && focusTraversal)
next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
[...]
}
One easy solution would be to disable focus traversals all together and set a com.badlogic.gdx.scenes.scene2d.ui.TextFieldListener that automatically does the traversals instead:
TextField textField
textField.setFocusTraversal(false);
textField.setTextFieldListener(new TextFieldListener() {
#Override
public void keyTyped(TextField textField, char key) {
if ((key == '\r' || key == '\n')){
textField.next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
}
}
});
If you need to be able to enable and disable focus traversals using TextFields setFocusTraversal method, there would also be a quite hacky solution by wrapping the internal InputListener inside your own listener when it is added to the TextField (but I would not recommend this):
class MyTextField extends TextField{
class InputWrapper extends InputListener{
private final InputListener l;
public InputWrapper(InputListener l) {
super();
this.l = l;
}
#Override
public boolean handle(Event e) {
return l.handle(e);
}
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
return l.touchDown(event, x, y, pointer, button);
}
#Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
l.touchUp(event, x, y, pointer, button);
}
#Override
public void touchDragged(InputEvent event, float x, float y,
int pointer) {
l.touchDragged(event, x, y, pointer);
}
#Override
public boolean mouseMoved(InputEvent event, float x, float y) {
return l.mouseMoved(event, x, y);
}
#Override
public void enter(InputEvent event, float x, float y, int pointer,
Actor fromActor) {
l.enter(event, x, y, pointer, fromActor);
}
#Override
public void exit(InputEvent event, float x, float y, int pointer,
Actor toActor) {
l.exit(event, x, y, pointer, toActor);
}
#Override
public boolean scrolled(InputEvent event, float x, float y,
int amount) {
return l.scrolled(event, x, y, amount);
}
#Override
public boolean keyDown(InputEvent event, int keycode) {
return l.keyDown(event, keycode);
}
#Override
public boolean keyUp(InputEvent event, int keycode) {
return l.keyUp(event, keycode);
}
#Override
public boolean keyTyped(InputEvent event, char character) {
if (isDisabled()) {
return false;
} else if ((character == '\r' || character == '\n')){
next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
return true;
}
return l.keyTyped(event, character);
}
}
public MyTextField(String text, Skin skin, String styleName) {
super(text, skin, styleName);
}
public MyTextField(String text, Skin skin) {
super(text, skin);
}
public MyTextField(String text, TextFieldStyle style) {
super(text, style);
}
boolean initialized = false;
#Override
public boolean addListener (EventListener l) {
if (!initialized) {
if (!(l instanceof InputListener)) {
throw new IllegalStateException();
}
initialized = true;
return super.addListener(new InputWrapper((InputListener) l));
}
return super.addListener(l);
}
}
edit:
On a second thought, you could also do this with the first solution by simply overwriting setFocusTraversal of the TextField and enabling/disabling your own listener during calls to this method.
I found a workaround, but I'd still appreciate a cleaner solution that gets both platforms to behave the same way.
I added a flag to indicate whether the focus will move by default, and I only change the focus if it won't move on its own. I then set that flag from the MainActivity class for Android or the Main class for desktop. I've posted the complete sample code on GitHub.
private void nextField() {
TextField nextField =
stage.getKeyboardFocus() == text1
? text2
: stage.getKeyboardFocus() == text2
? text3
: text1;
nextField.setText("");
if ( ! isFocusMovedAutomatically) {
stage.setKeyboardFocus(nextField);
}
}
In gdx-1.9.4 I was able to do the following:
final TextField newMessageTextField = new TextField("", uiSkin){
#Override
protected InputListener createInputListener () {
return new TextFieldClickListener(){
#Override
public boolean keyUp(com.badlogic.gdx.scenes.scene2d.InputEvent event, int keycode) {
System.out.println("event="+event+" key="+keycode);
return super.keyUp(event, keycode);
};
};
}
};
After pressing the arrows with focus on the TextField I've got
event=keyUp key=19
event=keyUp key=20
event=keyUp key=22
event=keyUp key=21
Related
I have a clicklistener extended class which aims to cached any current actions of the actor during touchDown, and assigns it back when touchUp is triggered. However, it does not works for sequence or parallel actions.
public class MyClickListener extends ClickListener {
public Actor actor;
private final Array<Action> cachedActions = new Array<Action>();
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
super.touchUp(event, x, y, pointer, button);
actor = event.getListenerActor();
actor.addAction(btnScaleBackActions());
for(Action action:cachedActions)
{
//action.reset(); // i wants the actor to continue at where it stop
action.setTarget(actor);
action.setActor(actor);
actor.addAction(action);
}
cachedActions.clear();
}
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if(pointer==0)
{
actor = event.getListenerActor();
actor.setScale(0.9f);
cachedActions.addAll(actor.getActions());
actor.clearActions();
return super.touchDown(event, x, y, pointer, button);
}
else
{
return false;
}
}
My buttons testing:
// button touchUp continue its previous action at where it stop
btn1.addAction(Actions.scaleBy(1,1,3));
// button touchUp not continue it previous actions and complete stop
btn2.addAction(sequence(Actions.scaleBy(1,1,3)));
// button touchUp give nullException error
btn3.addAction(forever(Actions.scaleBy(1,1,3)));
//error :
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.badlogic.gdx.scenes.scene2d.actions.RepeatAction.delegate(RepeatAction.java:29)
at com.badlogic.gdx.scenes.scene2d.actions.DelegateAction.act(DelegateAction.java:43)
Is it possible to continue sequence/parallel actions at where it stop at myClickListener class?
Here's an alternate idea. Rather than deal with removing and restoring actions, and subsequently dealing with the pools issue, you can wrap your actions in a new type of pausable action.
public class PausableAction extends DelegateAction {
public static PausableAction pausable(Action wrappedAction){
PausableAction action = Actions.action(PausableAction.class);
action.setAction(wrappedAction);
return action;
}
boolean paused = false;
public void pause (){
paused = true;
}
public void unpause (){
paused = false;
}
protected boolean delegate (float delta){
if (paused)
return false;
return action.act(delta);
}
public void restart () {
super.restart();
paused = false;
}
}
Now when getting your actions, wrap them in a pausable, for example:
btn1.addAction(PausableAction.pausable(Actions.scaleBy(1,1,3)));
And pause/unpause actions when you need to, like:
//...
actor = event.getListenerActor();
actor.setScale(0.9f);
for (Action action : actor.getActions())
if (action instanceof PausableAction)
((PausableAction)action).pause();
return super.touchDown(event, x, y, pointer, button);
The default behavior of actions that came from a pool (like from the Actions class) is to restart themselves when they are removed from an actor. It's actually not safe for you to be reusing these instances because they have also been returned to the pool and might get attached to some other actor unexpectedly.
So before you remove them from your actor, you need to set their pools to null.
private static void clearPools (Array<Action> actions){
for (Action action : actions){
action.setPool(null);
if (action instanceof ParallelAction) //SequenceActions are also ParallelActions
clearPools(((ParallelAction)action).getActions());
else if (action instanceof DelegateAction)
((DelegateAction)action).getAction().setPool(null);
}
}
//And right before actor.clearActions();
clearPools(actor.getActions());
Then, when you add them back to the actor, you'll want to add their pools back so they can go back to the Actions pools and be reused later to avoid GC churn.
private static void assignPools (Array<Action> actions){
for (Action action : actions){
action.setPool(Pools.get(action.getClass()));
if (action instanceof ParallelAction)
assignPools(((ParallelAction)action).getActions());
else if (action instanceof DelegateAction){
Action innerAction = ((DelegateAction)action).getAction();
innerAction.setPool(Pools.get(innerAction.getClass()));
}
}
}
//And call it on your actor right after adding the actions back:
assignPools(actor.getActions);
I am trying to simplify the Javax swing graphics classes in order to make it easier for other people to get into Java graphics, but I am facing a problem with testing it.
Keep in mind, that I am writing the main method as a user of the code and not the developer. I need answers that will change the code of the class methods and not the main method.
What my main method code is supposed to do is print 'hovering' when the user hovers over the button. However, when I add a SOP statement before the if statement, it works...
The method for the mouse hovering is in the Button class.
Here is my main method code -
public static void main(String[] args) {
GraphWin win = new GraphWin(1000, 1000, "Graphics Window - Test");
win.show();
Button button = new Button(new Point(380, 300), new Point(620, 400));
button.draw(win);
enter code herewhile(true) {
//System.out.println(button.hovering);
if(button.hovering) {
System.out.println("hovering");
}
}
}
And here is my code for the Button class -
public class Button implements MouseListener{
public JButton button;
public boolean clicked = false, hovering = false, pressed = false;
public Button(Point p, Point p2) { //This is the default constructor of the button with only 2 points specified
this.button = new JButton();
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(Point p, Point p2, String text) { //This constructor requires text to be displayed`enter code here`
this.button = new JButton(text);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(String icon, Point p, Point p2) { //This constructor sets an Icon for the button
this.button = new JButton();
this.setIcon(icon);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(Point p, Point p2, String text, String icon) { //Here, both the text and Icon is specified
this.button = new JButton(text);
this.setIcon(icon);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public void draw(GraphWin win) {
win.window.add(this.button);}
public void setBounds(Point p, Point p2) {
this.button.setBounds(p.x, p.y, p2.x - p.x, p2.y - p.y);
}
public void setEnabled(boolean enable) {
this.button.setEnabled(enable);}
public void disable() {
this.button.setEnabled(false);}
public void enable() {
this.button.setEnabled(true);
}
public void setColor(Color color) {
this.button.setBackground(color);}
public void setColor(String color) {
this.button.setBackground(Color.decode(color));}
public void setText(String text) {
this.button.setText(text);}
public void setIcon(String icon) {
File imageCheck = new File(icon);
if(!imageCheck.exists())
System.out.println("Image file not found!");
else
this.button.setIcon(new ImageIcon(icon));
}
public void resizeIcon(String icon, int width, int height) {
Image img = new ImageIcon(icon).getImage();
img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
this.button.setIcon(new ImageIcon(img));
}
public void setCustomMargins(int top, int bottom, int left, int right) {
this.button.setMargin(new Insets(top, left, bottom, right));}
public void setMargins(int m) {
this.button.setMargin(new Insets(m, m, m, m));}
public void setLabel(String label) {
this.button.setToolTipText(label);
}
public void setBorderVisible(boolean border) {
this.button.setBorderPainted(border);}
public void setOpaque(boolean opaque) {
this.button.setContentAreaFilled(opaque);}
#Override
public void mouseEntered(MouseEvent arg0) {
this.hovering = true;
System.out.println(1);
}
#Override
public void mouseExited(MouseEvent arg0) {
this.hovering = false;
}
#Override
public void mousePressed(MouseEvent arg0) {
this.pressed = true;
}
#Override
public void mouseReleased(MouseEvent arg0) {
this.pressed = false;
}
#Override
public void mouseClicked(MouseEvent e) {
this.clicked = true;
System.out.println(1);
}
}
This sort of thing is usually to do with threading.
Events in Swing are dispatched on the AWT Event Dispatch Thread (EDT). In order to be thread-safe, practically everything dealing with Swing/AWT should be done on the EDT.
In your case, there is no kind of locking between the variable being set and read. Adding a println causes a pause (with all sorts of memory barriers or whatnot) that happens to allow the program to run in the desired sequence.
You've probably seen main methods written to pass execution straight over to the AWT.
class MyGUI {
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(MyGUI::go);
}
private static void go() {
...
It might be better to supply the main class yourself, implemented such that it takes the application class as an argument and passes execution on once everything is setup. Whilst traditionally command lines use a main static method/function, everywhere else subtypes: Applets, Servlets, etc.
best approach would be to use a isHovering() method but educated guess on the behavior inside a while(true) with or without a Sysout might be related to a compiler optimisation. Might be fixed by putting the hovering variable as transient
When i click SelectBox first time and type some key, then my method CreateAutoComplete is execute one time. When i unfocus SelectBox and click again and type some key, then method execute two times. Next three times... four...
Of course i want only one time everytime.
private SelectBox<String> sbNationality;
private AutoComplete auto = new AutoComplete();
...
sbNationality.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
addListener(new ClickListener() {
#Override
public boolean keyTyped(InputEvent event, char character) {
auto.CreateAutoComplete(sbNationality, character);
return super.keyTyped(event, character);
}
});
super.clicked(event, x, y);
}
});
Your listener is registering a new listener at each click event, this is why you get duplicate actions.
It sems that the ClickListener you use, has a keyTyped method which is what interests you (the key typed event), not the click event by itself, so try this :
addListener(new ClickListener() {
#Override
public boolean keyTyped(InputEvent event, char character) {
auto.CreateAutoComplete(sbNationality, character);
return super.keyTyped(event, character);
}
});
Here's a simple example, most likely not meeting your requirements, but should give you the general idea.
// Add this field as a flag for you to know whether the sbNationality has been clicked or not.
private Boolean isSbNationalityClicked = false;
// On click, set the flag to true
sbNationality.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y){
isSbNationalityClicked = true;
super.clicked(event, x, y);
}
});
addListener(new ClickListsner(){
#Override
public boolean keyTyped(InputEvent, char characer){
// if you caught the keyTyped event and the flag is true - perform your desired action
if(isSbNationalityClicked){
auto.CreateAutoComplete(sbNationality, character);
// set flag to false, since the desired action has been executed
// you might want to set flag to false in some other cases as well,
// like mouseReleased or mouseLeave
isSbNationalityClicked = false;
return super.keyTyped(event, character);
}
}
});
Using the code above, once your sbNationality gets clicked, it'll set the flag to true, so no matter what you do meanwhile, keyTyped listener will act like it was clicked. You might wanna catch some other events like mouseReleased or mouseLeave to handle the value of isSbNationalityClicked flag.
I'm trying to get when the mouse just clicked, not when the mouse is pressed.
I mean I use a code in a loop and if I detect if the mouse is pressed the code will execute a lot of time, but I want execute the code only Once, when the mouse has just clicked.
This is my code :
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
//Some stuff
}
See http://code.google.com/p/libgdx/wiki/InputEvent - you need to handle input events instead of polling, by extending InputProcessor and passing your custom input processor to Gdx.input.setInputProcessor().
EDIT:
public class MyInputProcessor implements InputProcessor {
#Override
public boolean touchDown (int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
// Some stuff
return true;
}
return false;
}
}
And wherever you want to use that:
MyInputProcessor inputProcessor = new MyInputProcessor();
Gdx.input.setInputProcessor(inputProcessor);
If find it easier to use this pattern:
class AwesomeGameClass {
public void init() {
Gdx.input.setInputProcessor(new InputProcessor() {
#Override
public boolean TouchDown(int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
onMouseDown();
return true;
}
return false
}
... the other implementations for InputProcessor go here, if you're using Eclipse or Intellij they'll add them in automatically ...
});
}
private void onMouseDown() {
}
}
You can use Gdx.input.justTouched(), which is true in the first frame where the mouse is clicked. Or, as the other answer states, you can use an InputProcessor (or InputAdapter) and handle the touchDown event:
Gdx.input.setInputProcessor(new InputAdapter() {
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (button == Buttons.LEFT) {
// do something
}
}
});
Without InputProcessor you can use easy like this in your render loop:
#Override
public void render(float delta) {
if(Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)){
//TODO:
}
}
I would like to make the point info tooltip appear faster. How can i do it? with the default setting I have to hover the mouse onto the point, then wait to be able to see point coordinate information. I want the point coordinates to be immediately available. How can i do that?
ChartPanel provides getInitialDelay() and setInitialDelay() to query and alter "the initial tooltip delay value used inside this chart panel." As a concrete example based on BarChartDemo1, the following change to the constructor eliminates the initial delay entirely:
public BarChartDemo1(String title) {
super(title);
…
chartPanel.setInitialDelay(0);
setContentPane(chartPanel);
}
It's a late solution but here it is.
Here is how i handled with JavaFX.
It shows the tooltip fast instant and tooltip does not fade after a while.
/**
* The "tooltip" is the hard-coded id for the tooltip object.
* It's set inside the JFreeChart Lib.
* */
public static String TOOLTIP_ID = "tooltip";
public static void removeTooltipHandler(ChartViewer chartViewer) {
chartViewer.getCanvas().removeAuxiliaryMouseHandler(chartViewer.getCanvas().getMouseHandler(TOOLTIP_ID));
}
public static void addFasterTooltipHandler(ChartViewer chartViewer) {
if(chartViewer.getCanvas().getMouseHandler(TOOLTIP_ID) != null) {
removeTooltipHandler(chartViewer);
}
chartViewer.getCanvas().addAuxiliaryMouseHandler(new TooltipHandlerFX(TOOLTIP_ID) {
Tooltip tooltip;
boolean isVisible = false;
#Override
public void handleMouseMoved(ChartCanvas canvas, MouseEvent e) {
if (!canvas.isTooltipEnabled()) {
return;
}
String text = getTooltipText(canvas, e.getX(), e.getY());
setTooltip(canvas, text, e.getScreenX(), e.getScreenY());
}
private String getTooltipText(ChartCanvas canvas, double x, double y) {
ChartRenderingInfo info = canvas.getRenderingInfo();
if (info == null) {
return null;
}
EntityCollection entities = info.getEntityCollection();
if (entities == null) {
return null;
}
ChartEntity entity = entities.getEntity(x, y);
if (entity == null) {
return null;
}
return entity.getToolTipText();
}
// This function is copied from Canvas.setTooltip and manipulated as needed.
public void setTooltip(ChartCanvas canvas, String text, double x, double y) {
if (text != null) {
if (this.tooltip == null) {
this.tooltip = new Tooltip(text);
this.tooltip.autoHideProperty().set(false); // Disable auto hide.
Tooltip.install(canvas, this.tooltip);
} else {
this.tooltip.setText(text);
this.tooltip.setAnchorX(x);
this.tooltip.setAnchorY(y);
}
this.tooltip.show(canvas, x, y);
isVisible = true;
} else {
if(isVisible) {
this.tooltip.hide();
isVisible = false;
}
}
}
});
}