Text editor base?

I already look this option but for my goal your code is better.

2 Likes

Interesting project.

Processing’s text display primitives are not designed with text editing in mind.

If you want to mix text editor features with Processing features (drawing / multimedia etc.) then you can incorporate Java swing / awt in order to include non-processing text edit and display components. Here is a good previous discussion with @quark that includes practical examples.

If you just want to write a text editor like Notepad++, and you want to use a Processing-like language, I’d recommend doing it directly in Java. There are plenty of starter “text editor in Java” projects out there that you can use as a beginning template, then customize with the features you want.

Trying to create a programming text editor inside a Processing sketch is a bit like trying to build a swimming pool inside a limousine. It is absolutely possible! …just more difficult and more limited.

4 Likes

I’m a little late so it looks like the problem has been solved already, but thanks for the compliment haha. I just used an array of strings with a cursor x and y location, the x being the character in the string and the y being the string in the array.

1 Like

Did you work further on this?

Can you share your code please?

1 Like

Sorry for the very very late reply. I was busy with school then I forgot.
I did. I found this code from 2012 and modified it a bit:

/**
 * Multiline Text Box
 * 
 * 2012 William Smith
 * aka calsign
 * 
 * Implementation requires mousePressed(), mouseDragged(),
 * mouseReleased(), and keyPressed() to call their respective
 * functions in the class. This class has been extracted from one of my
 * ongoing projects and is in no way complete, although it features
 * numerous ideas that may be of use to beginners in this area.
 * 
 * Text box supports basic text input, multiple lines, lines returning when
 * becoming too long (TODO), selection, and copy + paste.
 **/
 
class MultilineTextBox {
  String prompt;
  String[] text;
  int xpos;
  int ypos;
 
  boolean alreadyPressed;
  boolean selecting;
  int xpos2;
  int ypos2;
 
  PVector loc;
 
  float WIDTH;
  float HEIGHT;
 
  boolean inverseBackground;
  boolean hasFocus;
  boolean hasInputFocus;
 
  int lastPress = -500;
 
  MultilineTextBox(String prompt, float x, float y, float w, float h) {
    WIDTH = w;
    HEIGHT = h;
 
    this.prompt = prompt;
    text = new String[1];
    text[0] = "";
    ypos = 0;
    xpos = 0;
 
    loc = new PVector(x, y);
  }
 
  MultilineTextBox(String prompt, float x, float y, float w, float h, boolean inverseBackground) {
    WIDTH = w;
    HEIGHT = h;
 
    this.prompt = prompt;
    text = new String[1];
    text[0] = "";
    ypos = 0;
    xpos = 0;
 
    loc = new PVector(x, y);
 
    this.inverseBackground = inverseBackground;
  }
  
  void setXY(float x, float y) {
    loc = new PVector(x, y);
  }
 
  void update() {
  }
 
  void updatePress() {
    if (mousePressed) {
      if (mouseX > loc.x && mouseX < loc.x + WIDTH && mouseY > loc.y && mouseY < loc.y + HEIGHT) {
        hasFocus = true;
        hasInputFocus = true;
 
        testPos();
 
        alreadyPressed = true;
      } 
      else {
        hasFocus = (text.length <= 1 && text[0].length() <= 0 ? false : true);
        hasInputFocus = false;
        if (!alreadyPressed) selecting = false;
      }
    }
  }
 
  void updateDrag() {
    if (mouseX > loc.x && mouseX < loc.x + WIDTH && mouseY > loc.y && mouseY < loc.y + HEIGHT) testPos();
    else if (selecting) testPos();
  }
 
  void updateRelease() {
    alreadyPressed = false;
  }
 
  void updateKeys() {
    
    if (keyPressed) {
      if (key == CODED) {
        if (millis() - lastPress > 0) {
          if (keyCode == LEFT) {
            if (xpos <= 0 && ypos > 0) {
              ypos --;
              xpos = text[ypos].length();
            } 
            else xpos = constrain(xpos - 1, 0, text[ypos].length());
          }
          if (keyCode == RIGHT) {
            if (xpos >= text[ypos].length() && ypos < text.length - 1) {
              ypos ++;
              xpos = 0;
            } 
            else xpos = constrain(xpos + 1, 0, text[ypos].length());
          }
          if (keyCode == UP && ypos > 0) {
            ypos --;
            xpos = constrain(xpos, 0, text[ypos].length());
          }
          if (keyCode == DOWN && ypos < text.length - 1) {
            ypos ++;
            xpos = constrain(xpos, 0, text[ypos].length());
          }
          if (keyCode == KeyEvent.VK_HOME) xpos = 0;
          if (keyCode == KeyEvent.VK_END) xpos = text[ypos].length();
 
          if (!(keyCode == SHIFT)) {
            ypos2 = ypos;
            xpos2 = xpos;
          }
 
          lastPress = millis();
        }
      } 
      else {
        switch(key) {
        case ESC:
        case TAB:
          break;
        case ENTER:
        case RETURN:
          newline();
          break;
        case BACKSPACE:
        case DELETE:
          if (selecting && (ypos != ypos2 || xpos != xpos2)) {
            int minypos = min(ypos, ypos2);
            int maxypos = max(ypos, ypos2);
 
            int minxpos;
            int maxxpos;
 
            if (minypos == maxypos) {
              minxpos = min(xpos, xpos2);
              maxxpos = max(xpos, xpos2);
            } 
            else {
              minxpos = (minypos == ypos ? xpos : xpos2);
              maxxpos = (maxypos == ypos ? xpos : xpos2);
            }
 
            if (minypos == maxypos) text[ypos] = text[ypos].substring(0, minxpos) + text[ypos].substring(maxxpos, text[ypos].length());
            else {
              String combine = text[minypos].substring(0, minxpos) + text[maxypos].substring(maxxpos, text[maxypos].length());
              String[] pre = append(subset(text, 0, minypos), combine);
              text = concat(pre, subset(text, maxypos + 1, text.length - (maxypos + 1)));
            }
 
            ypos = minypos;
            xpos = minxpos;
 
            ypos2 = ypos;
            xpos2 = xpos;
            selecting = false;
 
            lastPress = millis();
          } 
          else {
            if (millis() - lastPress > 0 && xpos > 0) {
              text[ypos] = text[ypos].substring(0, xpos - 1) + text[ypos].substring(xpos, text[ypos].length());
 
              xpos --;
              xpos2 = xpos;
 
              lastPress = millis();
            } 
            else if (ypos > 0 && xpos == 0) {
              int over = text[ypos - 1].length();
              String combine = text[ypos - 1] + text[ypos];
 
              String[] pre = append(subset(text, 0, ypos - 1), combine);
              text = concat(pre, subset(text, ypos + 1, text.length - (ypos + 1)));
 
              ypos --;
              xpos = over;
 
              ypos2 = ypos;
              xpos2 = xpos;
            }
          }
          break;
        default:
          if (selecting && (ypos != ypos2 || xpos != xpos2)) {
            int minypos = min(ypos, ypos2);
            int maxypos = max(ypos, ypos2);
 
            int minxpos;
            int maxxpos;
 
            if (minypos == maxypos) {
              minxpos = min(xpos, xpos2);
              maxxpos = max(xpos, xpos2);
            } 
            else {
              minxpos = (minypos == ypos ? xpos : xpos2);
              maxxpos = (maxypos == ypos ? xpos : xpos2);
            }
            
            println(text[ypos].substring(minxpos,maxxpos));
 
            if (millis() - lastPress > 0) {
 
              if (minypos == maxypos) text[ypos] = text[ypos].substring(0, minxpos) + key + text[ypos].substring(maxxpos, text[ypos].length());
              else {
                String combine = text[minypos].substring(0, minxpos) + key + text[maxypos].substring(maxxpos, text[maxypos].length());
                String[] pre = append(subset(text, 0, minypos), combine);
                text = concat(pre, subset(text, maxypos + 1, text.length - (maxypos + 1)));
              }
 
              ypos = minypos;
              xpos = minxpos + 1;
 
              ypos2 = ypos;
              xpos2 = xpos;
              selecting = false;
 
              lastPress = millis();
            }
          } 
          else {
            if (millis() - lastPress > 0 && textWidth(text[ypos].substring(0, xpos) + key + text[ypos].substring(xpos, text[ypos].length())) < WIDTH - 8) {
              text[ypos] = text[ypos].substring(0, xpos) + key + text[ypos].substring(xpos, text[ypos].length());
 
              xpos ++;
              xpos2 = xpos;
 
              lastPress = millis();
            }
          }
          break;
        }
      }
    }
  }
 
  void newline() {
    String after = text[ypos].substring(xpos, text[ypos].length());
    text[ypos] = text[ypos].substring(0, xpos);
    text = splice(text, after, ypos + 1);
 
    ypos ++;
    xpos = 0;
 
    ypos2 = ypos;
    xpos2 = xpos;
  }
 
  void testPos() {
    if (alreadyPressed) {
      selecting = true;
 
      ypos2 = int(constrain((mouseY - loc.y) / 18.0, 0, text.length - 1));
 
      for (int i = 0; i < text[ypos2].length(); i ++) {
        if (mouseX - loc.x - 4 <= textWidth(text[ypos2].substring(0, i)) + textWidth(text[ypos2].charAt(i)) / 2) {
          xpos2 = i;
          return;
        }
      }
 
      xpos2 = text[ypos].length();
    } 
    else {
      selecting = false;
 
      ypos = int(constrain((mouseY - loc.y) / 18.0, 0, text.length - 1));
 
      for (int i = 0; i < text[ypos].length(); i ++) {
        if (mouseX - loc.x - 4 <= textWidth(text[ypos].substring(0, i)) + textWidth(text[ypos].charAt(i)) / 2) {
          xpos = i;
          return;
        }
      }
 
      xpos = text[ypos].length();
    }
  }
 
  void display() {
    
    strokeWeight(1);
    noStroke();
    rectMode(CORNER);
    
    noStroke();
    
    fill(0, 41, 96);
    rect(loc.x, loc.y, WIDTH, HEIGHT);
    
    stroke(255);
 
    textFont(createFont("Source Code Pro",15));
    textAlign(LEFT, TOP);
    if (hasFocus) fill(255);
    else fill(102);
 
    if (hasFocus) {
      for (int i = 0; i < text.length; i ++)
        text(text[i], loc.x + 4, loc.y + 6 + i * 18);
    } 
    else text(prompt, loc.x + 4, loc.y + 6);
 
    ypos = constrain(ypos, 0, text.length);
    ypos2 = constrain(ypos2, 0, text.length);
 
    xpos = constrain(xpos, 0, text[ypos].length());
    xpos2 = constrain(xpos2, 0, text[ypos2].length());
    
    if (keyPressed) line(loc.x + 5 + textWidth(text[ypos].substring(0, xpos)), loc.y + 4 + ypos * 18, (loc.x + 5 + textWidth(text[ypos].substring(0, xpos))), loc.y + 18 + ypos * 18);
 
    if (selecting && (xpos != xpos2 || ypos != ypos2)) {
      fill(162, 234, 255, 102);
      noStroke();
 
      int minypos = min(ypos, ypos2);
      int maxypos = max(ypos, ypos2);
 
      int minxpos;
      int maxxpos;
 
      if (minypos == maxypos) {
        minxpos = min(xpos, xpos2);
        maxxpos = max(xpos, xpos2);
      } 
      else {
        minxpos = (minypos == ypos ? xpos : xpos2);
        maxxpos = (maxypos == ypos ? xpos : xpos2);
      }
 
      if (minypos == maxypos) rect(loc.x + 4 + textWidth(text[minypos].substring(0, minxpos)), loc.y + 4 + minypos * 18, textWidth(text[maxypos].substring(0, maxxpos)) - textWidth(text[maxypos].substring(0, minxpos)), 18);
      else {
        for (int y = minypos; y <= maxypos; y ++) {
          for (int x = 0; x < text[y].length(); x ++) {
            if ((y == minypos ? x >= minxpos : true) && (y == maxypos ? x < maxxpos : true)) rect(loc.x + 4 + textWidth(text[y].substring(0, x)), loc.y + 4 + y * 18, textWidth(text[y].charAt(x)), 18);
          }
          if (text[y].length() <= 0) rect(loc.x + 4, loc.y + 4 + y * 18, textWidth(" ") / 2, 18);
        }
      }
    } 
    else if (hasInputFocus && millis() / 300 % 2 == 1) line(loc.x + 5 + textWidth(text[ypos].substring(0, xpos)), loc.y + 4 + ypos * 18, (loc.x + 5 + textWidth(text[ypos].substring(0, xpos))), loc.y + 18 + ypos * 18);
  }
 
  String getText(int i) {
    if (text.length > i) return text[i];
    else return "";
  }
 
  void setText(String toSet) {
    String[] output = new String[0];
    int last = -1;
    for (int i = 0; i < toSet.length(); i ++) {
      if (toSet.charAt(i) == '\n') {
        output = append(output, toSet.substring(last + 1, i));
        last = i;
      }
    }
 
    output = append(output, toSet.substring(last + 1, toSet.length()));
 
    text = output;
  }
 
  String consolidate() {
    String toReturn = "";
    for (int i = 0; i < text.length; i ++) {
      toReturn += text[i];
      if (i < text.length - 1) toReturn += "\n";
    }
 
    return toReturn;
  }
}

On the top comment it says it included copy/paste but it doesn’t so I was working on that and exporting the text with saveStrings(). It was coming out pretty good, I did a new gui and added some typing sounds then I thought about possibly trying to add syntax highlighting but I realized if I wanted to do that I should probably try to rewrite the top text area and make it so every word is stored in an array instead of every line. Then I saw @jeremydouglass’ comment and I decided to try my luck with java. Then I found some code on stackoverflow that does syntax highlighting by using a JTextPane with a StyledDocument. I took it, modified it and used it as a base. Then I added a tab system and made a custom undecorated JFrame. I also found a class which adds line numbers to a JTextPane.

It was coming out better than processing as far as function goes but it looked way worse since swing’s default look and feel doesn’t look very good. So I did some research and found FlatLaF. A custom look and feel for swing that looks way better than metal.

My current version looks like this:


and I’m pretty happy with it.

But I will definitely return back to the processing one after completing this one since I feel like java is more limited than processing on making interfaces. I just wish there was a way to use swing components with processing.

Links:
The text area: https://forum.processing.org/two/discussion/12094/does-anyone-have-a-textbox-function-i-could-use
Line numbers for JTextPane: https://tips4java.wordpress.com/2009/05/23/text-component-line-number/
I tried to find the post for the syntax highlighting but I couldn’t find the correct one, I will put it here if I find it.

3 Likes

Thanks a lot for this and thanks for coming back to me!

Chrisir

1 Like