How to write a CLASS for TEXT AREA without using any library?

edited October 2013 in Questions about Code

Hi all,

I working on a code for a text area or so called a text box. The version seems to work fine for a single box, but for multiple boxes it is too much code. In addition, if code is running in the browser, if you press "backspace" the page restarts on Windows computers and the written text is lost (due to html / javascript focus issues).

I kindly ask you to help me with two issues:

  1. to create a class similar to this function (not using any library, e.g. controlp5).

  2. to figure out a solution to the "Backspace" key problem when the code (javascirpt) running in the browser (Windows).

Here is my function:

//====================================================
//                  VARIABLES 
//----------------------------------------------------
final int sizeW                  =  600;
final int sizeH                  =  600;
String typedText                 = "";
color col;

//=========================================================
//           SETUP 
//.........................................................
void setup() {
  size(600, 600);
  smooth();
  col                  = color(#778C85);
}
//=========================================================
//           DRAW 
//.........................................................
void draw() {
  background(col);
  manageTextBox();
}
//=========================================================
//           FUNCTION
//.........................................................  
void manageTextBox() {
  background(col);
  String taLabel = "Please, enter your comments here: (use \"Right\" arrow to delete)";
  int taPadX = 10;
  int taX = sizeW/2;
  int taY = sizeH-sizeH/3; 
  int taSizeX = sizeW-sizeW/4;
  int taSizeY = sizeH/4; 
  color taBaseColor = color(255, 30); 
  color taBorderColor = color(255, 100); 
  color taLabelColor = color(255, 200); 
  color taTextColor = color(0, 200); 
  stroke(taBorderColor);
  strokeWeight(1); 
  fill(taBaseColor);
  rectMode(CENTER);
  rect(taX, taY, taSizeX, taSizeY); 
  fill(taLabelColor);
  rectMode(CORNER);
  textAlign(LEFT);
  text(taLabel, sizeW/2 - (sizeW-sizeW/4)/2, sizeH-sizeH/3 - (sizeH/4)/2-taPadX*2, taSizeX, taSizeY);
  fill(taTextColor);
  text(typedText+(frameCount/10 % 2 == 0 ? "_" : ""), sizeW/2 - (sizeW-sizeW/4)/2, sizeH-sizeH/3 - (sizeH/4)/2, taSizeX-2*taPadX, taSizeY-2*taPadX);   
} 

//=========================================================
//           KEY INPUTS FOR TEXT
//.........................................................
void keyPressed() {
  if ((key != CODED)) {
    switch(key) {
    case BACKSPACE:
      typedText = typedText.substring(0, Math.max(0, typedText.length()-1));
      break;
    case TAB:
      typedText += "    ";
      break;
    case ENTER:
    case RETURN:
      typedText += "\n"; //enable line breaks
      break;
    case ESC: 
    case DELETE:
      break;
    default:
      typedText += str(key);
    }
  }
  else if ((key == CODED)) {
    if (keyCode == LEFT) {
      typedText = typedText.substring(0, Math.max(0, typedText.length()-1));
    }
  }
  if (key == ESC) { 
    key = 0;
  }
}

Thanks in advance!

Answers

  • edited October 2013

    Hey! I've come up w/ a class very similar to your code above. Rest is up to ya! =:)
    Anyways, tested it online w/ Firefox, Midori, Opera & Chromium under Lubuntu 13.04 (64-bit):

    • Only Firefox was able to detect both TAB & BACKSPACE.
    • Midori got BACKSPACE only.
    • Both Opera & Chromium have got neither right!

    You can also check it online below:
    http://studio.processingtogether.com/sp/pad/export/ro.9Zo$UbIWYZEDR/latest

    And here's the source code: =P~ See next post...

  • edited January 2014 Answer ✓

    A newer version! Now w/ keyPressed() using LEFT & RIGHT as additional band-aid for BACKSPACE & TAB! :bz
    Also, DELETE clears a TextBox and lim to limit how many chars! :ar!

    http://studio.processingtogether.com/sp/pad/export/ro.9Zo$UbIWYZEDR/latest

    /** 
     * TextBox Writer (v2.4)
     * by  Inarts (2013/Oct)
     * mod GoToLoop
     *
     * forum.processing.org/two/discussion/423/
     * how-to-write-a-class-for-text-area-without-using-any-library
     *
     * studio.processingtogether.com/sp/pad/export/ro.9Zo$UbIWYZEDR/latest
     */
    
    static final int NUM = 2;
    final TextBox[] tboxes = new TextBox[NUM];
    int idx;
    
    void setup() {
      size(640, 480);
      frameRate(20);
      smooth(4);
    
      rectMode(CORNER);
      textAlign(LEFT);
      strokeWeight(1.5);
    
      instantiateBoxes();
      tboxes[idx = 1].isFocused = true;
    }
    
    void draw() {
      background(#778C85);
      for (int i = 0; i != NUM; tboxes[i++].display());
    }
    
    void mouseClicked() {
      int i = idx = -1;
      while (++i != NUM)  if (tboxes[i].checkFocus())  idx = i;
    }
    
    void keyTyped() {
      final char k = key;
      if (k == CODED | idx < 0)  return;
    
      final TextBox tbox = tboxes[idx];
      final int len = tbox.txt.length();
    
      if (k == BACKSPACE)  tbox.txt = tbox.txt.substring(0, max(0, len-1));
      else if (len >= tbox.lim)  return;
      else if (k == ENTER | k == RETURN)     tbox.txt += "\n";
      else if (k == TAB & len < tbox.lim-3)  tbox.txt += "    ";
      else if (k == DELETE)  tbox.txt = "";
      else if (k >= ' ')     tbox.txt += str(k);
    }
    
    void keyPressed() {
      if (key != CODED | idx < 0)  return;
      final int k = keyCode;
    
      final TextBox tbox = tboxes[idx];
      final int len = tbox.txt.length();
    
      if (k == LEFT)  tbox.txt = tbox.txt.substring(0, max(0, len-1));
      else if (k == RIGHT & len < tbox.lim-3)  tbox.txt += "    ";
    }
    
    void instantiateBoxes() {
      tboxes[0] = new TextBox(
      width>>2, height/4 + height/16, // x, y
      width - width/2, height/2 - height/4 - height/8, // w, h
      215, // lim
      0300 << 030, color(-1, 040), // textC, baseC
      color(-1, 0100), color(#FF00FF, 0200)); // bordC, slctC
    
      tboxes[1] = new TextBox(
      width>>3, height/2 + height/8, // x, y
      width - width/4, height - height/2 - height/4, // w, h
      640, // lim
      0300 << 030, color(-1, 040), // textC, baseC
      color(-1, 0100), color(#FFFF00, 0200)); // bordC, slctC
    }
    
    class TextBox { // demands rectMode(CORNER)
      final color textC, baseC, bordC, slctC;
      final short x, y, w, h, xw, yh, lim;
    
      boolean isFocused;
      String txt = "";
    
      TextBox(int xx, int yy, int ww, int hh, int li, 
      color te, color ba, color bo, color se) {
        x = (short) xx;
        y = (short) yy;
        w = (short) ww;
        h = (short) hh;
    
        lim = (short) li;
    
        xw = (short) (xx + ww);
        yh = (short) (yy + hh);
    
        textC = te;
        baseC = ba;
        bordC = bo;
        slctC = se;
      }
    
      void display() {
        stroke(isFocused? slctC : bordC);
        fill(baseC);
        rect(x, y, w, h);
    
        fill(textC);
        text(txt + blinkChar(), x, y, w, h);
      }
    
      String blinkChar() {
        return isFocused && (frameCount>>2 & 1) == 0 ? "_" : "";
      }
    
      boolean checkFocus() {
        return isFocused = mouseX > x & mouseX < xw & mouseY > y & mouseY < yh;
      }
    }
    
  • edited October 2013

    @GoToLoop, thank you very much for your timely help! Your solution is very elegant and advanced!

  • edited October 2013

    About making a class: see the Objects tutorial on the Processing site.

    Make a new class. Declare as field all the global variables at the top of your code. Make your drawing logic a display() (or other name) method in the class. Add methods to handle user input.

    That's the delicate part: the draw() function of any sketch using your textareas must call the keyPressed() method inside the class, so it updates its content.

    A cleaner method would be for the class to register itself to the PApplet events, but I fear this won't work in JS mode.

    [EDIT] Woops, I didn't notice GoToLoop already did the objectification of the entity... But as I wrote, I would move the logic of key handling to the class itself. It is a bit messy to manipulate the inners of a class from outside, like this. :-)

  • edited October 2013

    @PhiLho, thank you for your suggestions! I do need this applet to work in JS mode, so I guess this is the only way. However, you suggest to "move the logic of key handling to the class itself ".

    Do you mean to create functions for key handling inside of a class?

    And later place these functions inside void keyPressed() {} and void keyTyped() {}?

    This is likely an excellent tip, but I am afraid to accidentally mess up the code that appears to be working.

  • edited October 2013 Answer ✓
    /**
     * TextBox Writer (v2.36+)
     * by  Inarts (2013/Oct)
     * mod GoToLoop & PhiLho
     *
     * forum.processing.org/two/discussion/423/how-to-write-a-class-for-text-area-without-using-any-library
     */
    
    final int NUM = 2;
    final TextBox[] tboxes = new TextBox[NUM];
    
    void setup() {
      size(640, 480);
      frameRate(20);
      smooth();
    
      rectMode(CENTER);
    
      instantiateBoxes();
    }
    
    void draw() {
      background(#778C85);
      noStroke();
      fill(random(255), random(255), random(255));
      rect(random(width / 4, 3 * width / 4), random(height / 4, 3 * height / 4), random(width / 4), random(height / 4));
    
      for (TextBox tb : tboxes)
        tb.display();
    }
    
    void mouseClicked() {
      for (TextBox tb : tboxes)
        tb.checkFocus();
    }
    
    void keyTyped() {
      for (TextBox tb : tboxes)
        tb.keyTyped();
    }
    
    void keyPressed() {
      for (TextBox tb : tboxes)
        tb.keyPressed();
    }
    
    void instantiateBoxes() {
      tboxes[0] = new TextBox(
          width/4, height/4 + height/16, // x, y
          width - width/2, height/2 - height/4 - height/8, // w, h
          215, // lim
          0300 << 030, color(-1, 040), // textC, baseC
          color(-1, 0100), color(#FF00FF, 0200)); // bordC, slctC
    
      tboxes[1] = new TextBox(
          width/8, height/2 + height/8, // x, y
          width - width/4, height - height/2 - height/4, // w, h
          640, // lim
          0300 << 030, color(-1, 040), // textC, baseC
          color(-1, 0100), color(#FFFF00, 0200)); // bordC, slctC
    }
    
    class TextBox {
      final color textC, baseC, bordC, slctC;
      final int x, y, w, h, xw, yh, lim;
    
      boolean isFocused;
      String txt = "";
    
      TextBox(int xx, int yy, int ww, int hh, int li,
          color te, color ba, color bo, color se) {
        x = xx;
        y = yy;
        w = ww;
        h = hh;
    
        lim = li;
    
        xw = x + ww;
        yh = yy + hh;
    
        textC = te;
        baseC = ba;
        bordC = bo;
        slctC = se;
      }
    
      void display() {
        pushStyle();
    
        rectMode(CORNER);
        textAlign(LEFT);
        strokeWeight(1.5);
    
        stroke(isFocused ? slctC : bordC);
        fill(baseC);
        rect(x, y, w, h);
    
        fill(textC);
        text(txt + blinkChar(), x, y, w, h);
    
        popStyle();
      }
    
      void keyTyped() {
        if (!isFocused)
          return;
        if (key == CODED)
          return;
    
        final int len = txt.length();
    
        if (key == BACKSPACE) txt = txt.substring(0, max(0, len-1));
        else if (len >= lim)  return;
        else if (key == ENTER || key == RETURN)     txt += "\n";
        else if (key == TAB && len < lim-3) txt += "    ";
        else if (key == DELETE) txt = "";
        else if (key >= ' ')    txt += str(key);
      }
    
      void keyPressed() {
        if (!isFocused)
          return;
        if (key != CODED)
          return;
    
        final int len = txt.length();
    
        if (keyCode == LEFT) txt = txt.substring(0, max(0, len-1));
        else if (keyCode == RIGHT && len < lim-3) txt += "    ";
      }
    
      void checkFocus() {
        isFocused = mouseX > x && mouseX < xw && mouseY > y && mouseY < yh;
      }
    
      private String blinkChar() {
        return isFocused && (frameCount>>2 & 1) == 0 ? "_" : "";
      }
    }
    
  • edited October 2013 Answer ✓

    I wrote the code above while you were writing your answer... :-)

    One advantage of the new approach is that the sketch only keep track of the text boxes and doesn't care to keep track which one has focus.

    http://jsfiddle.net/Vjqcx/21/
    I fear my code doesn't work in JS mode, I will see later what went wrong...

    [EDIT] Works in Chrome, looks like Firefox doesn't like foreach loops... [EDIT] Work in Firefox when replacing

    for (TextBox tb : tboxes)
      tb.keyPressed();
    

    with

    for (int i = 0; i < tboxes.length; i++)
      tboxes[i].keyPressed();
    

    and similar. http://jsfiddle.net/Vjqcx/23/

    I didn't know this limitation in Firefox!

  • edited October 2013

    :) @PhiLho, you were quick to reedit the code! It is, indeed, an interesting problem! Your code works in Chrome, but if I press "Delete" it goes back to the initial search page... That would not work, as some people might use "delete" key because of habit and restart a page.

    Yes, your code now works in Firefox too! Thanks!

    I agree on moving style functions into a class:

        pushStyle();
        rectMode(CORNER);
        textAlign(LEFT);
        strokeWeight(1.5);
        ....
        popStyle();
    

    EDITED: Not flickering/blinking anymore, it was a glitch in the other part of the sketch.

  • edited October 2013 Answer ✓

    I didn't know this limitation in Firefox!

    I knew it b/c I've been testing Java to JS conversion for a long time now! b-(
    But I wouldn't be so sure about saying it's a "limitation" though! :(|)

    ... looks like Firefox doesn't like foreach loops...

    Well, only for the Array data-type! The other composite structures work as always have! B-)
    About Firefox v18 (take or remove 2 versions :-/), it got a little more strict in its JS than other browsers it seems.

    Anyways, Processing.JS has already addressed that case "centuries" ago! It's just that they refuse to release a stable version!
    And we and other host sites are stuck w/ JS Mode v1.4.1 like it's forever! X(

    In short, I wanted to use the enhanced for loop like this:

    for (TextBox tb: tboxes)   tb.display();
    

    But I've already knew that would break Firefox's compatibility: :((

    for ( int i = 0; i != NUM; tboxes[i++].display() );
    
  • edited October 2013 Answer ✓

    I agree on moving style functions into a class:

    pushStyle();
    rectMode(CORNER);
    textAlign(LEFT);
    strokeWeight(1.5);
    // ...
    popStyle();
    

    In the point of view of OOP and encapsulation it's gr8! But it's a redundant portion nonetheless! [-(

    You see, Processing has a setup() function just for preparing things before the "infinite" draw() "big loop"!
    But I can't see anywhere those settings change to something else after setup()! X_X

    Knowing your code is for running inside a browser, I've chosen the fastest performance algorithms and settings.
    Like frameRate(20), to avoid rendering canvas too many times like default 60 FPS.
    And if you didn't need that blinking cursor, I'd turn it off w/ noLoop()! :ar!

    I keep lotsa tabs open myself. And if I had any app(s) running, I'd like them to use only the minimum necessary resources to work!

  • "I can't see anywhere those settings change to something else after setup()!"
    Well, look at the start of my draw() function, I added this code specially for demonstrating the usefulness of pushStyle...

    In a library (and, ideally, in any class):

    1. You cannot ask users to set for you the settings / style you want / need to use.
    2. You can expect your library to work inside a complex sketch, doing various things.

    So I simulated this by adding this stupid code. :-)

  • "One issue with your version, if you type... the box is blinking"
    I don't see that in Firefox on Windows. Supposing you are not talking about the random rect() drawn all over the sketch area...

  • edited October 2013

    @PhiLho, regarding the inner flickering when typing inside the box... I tried to investigated it further, and you are right, by itself it is running nicely in Firefox. It was something wrong within the rest of my extended sketch causing this malfunction. Also, I understand the purpose and appreciate you including these colorful randomly-appearing rectangles.

    By the way @PhiLho modification works better when the text boxes are used in multiple pages. The first version of @GoToLoop was working perfectly on the first page, however, on the second page, I would not be able to to enter text even though the box would appear as selected and marker would be blinking.

    The modification I would add to @PhiLho version is adding the following code inside the TextBox class:

         void reset() {
            txt = "";
          }
    

    and resetting the text boxes when the NextPage button is clicked inside mousePressed():

        for (int i = 0; i != NUM; i++){
              tboxes[i].reset();
              }  
    

    This way the text typed in the first page does not carry on to the next page.

  • edited October 2013

    If someone is interested in sending the input of the text box to JavaScript follow these steps to modify the above code:

    1. Add a the getString() function inside TextBox class:

      String getString() {
          return txt;
        } 
      
    2. Place this code outside the setup() and draw() functions:

      interface JavaScript {
        void processingToJavascript(String input0, String input1);
      }
      
      void bindJavascript(JavaScript js) {
        javascript = js;
      }
      
      JavaScript javascript;
      
    3. Add the following code inside your sketch (for instance, inside mousePressed() in conjunction with some "Submit" button):

       String txt0 = tboxes[0].getString();
       String txt1 = tboxes[1].getString();
      
       if (javascript!=null) {
              javascript.processingToJavascript2(txt0, txt1);
            }
      
Sign In or Register to comment.