Get user input with textbox class

Hi, I would like to input a text into a ‘textbox’ and save the input into a variable when for example enter is pressed. So far I have come up with the following code:
main.pde:

TextBox textBox1;

void setup(){
    size(1000, 600);
    background(0);

    textBox1 = new TextBox(200, 200, 500, 50);
}

void draw(){
    textBox1.draw();
}

And TextBox.pde:

class TextBox{
    TextBox(int x, int y, int boxWidth, int boxHeight){
        this.x = x;
        this.y = y;
        this.boxWidth = boxWidth;
        this.boxHeight = boxHeight;
    }


    void draw(){
        drawBox();
        drawText();
        getUserInput();
    }


    void drawBox(){
        stroke(205);
        fill(205);
        rect(x, y, boxWidth, boxHeight);
    }

    void drawText(){
        textAlign(LEFT, CENTER);
        textSize(16);
        fill(255);
        text(textValue + getCursor(), x + 5, y + boxHeight/2);
    }

    void getUserInput(){
        if(!keyPressed){
            keyReleased = true;
            keyCounter = 0;
            previousKeyCounter = 0;
        }
        if (keyPressed && c != key){
            keyCounter = millis();

            c = key;

            if (c == BACKSPACE) textValue = "";
            else if (c >= ' ') textValue += str(c);
            if (textValue.length() > textLimit) textValue = "";

            previousKeyCounter = keyCounter;
            keyReleased = false;
        }

    }

    String getCursor() {
      return hovering() && (frameCount>>4 & 1) == 0 ? "|" : "";
    }

    boolean hovering(){
        return mouseX >= x && mouseX <= x + boxWidth && mouseY >= y && mouseY <= y + boxHeight;
    }

    private int x, y, boxWidth, boxHeight, textLimit = 40;
    private float currentValue, keyCounter, previousKeyCounter;
    private String textValue;
    private char keyInput, c;
    private boolean keyReleased;
}

For now let’s focus on the typing aspect first. As you can experience, typing fast does not work as expected, if you type character by character it does work. How can I include this functionality of pressing multiple keys at the same time? I would like the code to be modular, so nothing in functions like mouseClicked() or mouseReleased() etc.

If anyone can give me an example of how to use a library which has this functionality, that would be much appreciated as well. I don’t have a lot of knowledge on how to import libraries.

Thanks in advance,
Thomas

1 Like

When you look at the library controlP5 or g4p they come with a lot of examples

See section libraries on the website

You can install them via the library installer

They have good websites

Eg

http://www.lagers.org.uk/g4p/

1 Like

A fast typist will be hitting approximately 375 chars per minute, and Processing defaults to 60fps. However, no matter what its frameRate, Processing doesn’t drop these events. Even at 1fps, it will catch dozens of keys correctly in each frame, in the correct order. Try it!

void draw(){
  frameRate(1);
}
void keyReleased(){
  print(key);
}

Don’t invoke key from inside draw() -> textBox1.draw() -> getUserInput() – that is a convenience keyword and will only give you a single event per frame. Call it from keyReleased(), which is invoked by every single queued event. Pass the unique key value each time.

void keyReleased() {
  textBox1.getUserInput(key);
}
3 Likes

Thank you for the reply. Would it be possible to put the keyReleased() function inside a class?

Thank you for your answer. I installed the G4P library via the Processing IDE and made the following script using the examples provided in the IDE, to get a textfield:

import g4p_controls.*;


GTextField txf1;


public void setup() {
  size(500, 300);

  txf1 = new GTextField(this, 10, 10, 200, 20);
  txf1.setPromptText("Text field 1");
  txf1.setText("test1"); // works
}


public void draw() {
  background(200, 128, 200);

  if (keyPressed && key == ENTER){
    println(txf1.getText());
    txf1.setText("test2"); // gives nullpointerexception
    }
}

However, when I try to set a new text in the textfield, I get a nullpointer exception. The setText() function does work as expected in the setup, but not in the draw. Does anyone know how I can solve this problem?

1 Like

Maybe that’s the guilty line though?

Unfortunately, running the above script without

gives the same error:

java.lang.NullPointerException
at g4p_controls.GTextField.keyEvent(Unknown Source)
at g4p_controls.GWindowImpl.sendKeyEvent(Unknown Source)
at g4p_controls.GWindowImpl.keyEvent(Unknown Source)
at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at processing.core.PApplet$RegisteredMethods.handle(PApplet.java:1432)
at processing.core.PApplet.handleMethods(PApplet.java:1634)
NullPointerException
at processing.core.PApplet.handleKeyEvent(PApplet.java:3014)
at processing.core.PApplet.dequeueEvents(PApplet.java:2648)
at processing.core.PApplet.handleDraw(PApplet.java:2486)
at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1547)
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:313)

1 Like

yes, i see that too,
even i try not to use your keyPressed …
but the G4P internal event handler…
still.

1 Like

Looking at your sample code that uses GTextField there are a number of things worth mentioning but first of all the error is caused because you attempted to use setText("test2"); while the textfield still had focus (the text insertion cursor is flashing). If you enter some text in the textfield and then click outside the textfield before pressing ENTER then it works.

NEVER use setText(...) while the textfield has focus, same thing applies to GPassword and GTextArea.

All G4P control use event handlers to process events. Here is an example of how to use a textfield

import g4p_controls.*;


GTextField txf1;
String t0;

public void setup() {
  size(500, 300);

  txf1 = new GTextField(this, 10, 10, 200, 20);
  txf1.setPromptText("Text field 1");
  txf1.setText("test1"); // works
}

public void draw() {
  background(200, 128, 200);

  if (keyPressed && key == ENTER) {
    println(t0);
  }
}

public void handleTextEvents(GEditableTextControl textcontrol, GEvent event) {
  if(txf1 == textcontrol && event == GEvent.ENTERED){
    t0 = txf1.getText();
  }
}

You will find more information about G4P on my website also look at the G4P Programmer Guides

4 Likes

now thanks to that info you could overwrite the typed value?
or ( as i not fully understand your use case )
i try to empty it and give the user a new prompt text

public void handleTextEvents(GEditableTextControl textcontrol, GEvent event) {
  if ( textcontrol == txf1  && event == GEvent.ENTERED) {
    t0 = txf1.getText();
    println("ENTER: "+t0);
    txf1.setFocus(false);  //  @quark is the best
    //txf1.setText("over write new?");
    txf1.setText("");
    txf1.setPromptText("next value");
  }
}

even something like

public void handleTextEvents(GEditableTextControl textcontrol, GEvent event) {
  if ( textcontrol == txf1  && event == GEvent.ENTERED) {
    t[count] = txf1.getText();
    count++;
    txf1.setFocus(false); 
    txf1.setText("");
    txf1.setPromptText("next value t["+count+"]");
  }
}

No in the sense that setup, draw, and event handlers are part of the Processing loop. Draw is called and then events are processed, then draw is called again. You can’t put draw in a class, although you could choose to invoke redraw(). Your code puts event handlers in a class, but you then still need to pass the events from keyPressed() to the Objects of that class – for example, looping over an array in keyPressed and passing each event to each object.

OR you could use registerMethod("keyEvent") – for a previous discussion, see:

https://forum.processing.org/two/discussion/25477/capturing-keyevent-in-own-class-registermethod

1 Like

That was indeed what I was trying to achieve. Thank you

2 Likes