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.
