I’m trying to add functionality to a Swing JLabel and JTextArea such that:
- The user is only allowed to enter 500 characters into the textarea (max)
- The label contains a string message telling the user how many characters they have left (after every key stroke or backspace)
- When the components initialize the label reads “500 characters maximum!”
- For the first 500 characters typed, for every keystroke (a – z, A – Z, 0 – 9, and punctuation) typed, the label reads “x characters remaining”, where
xis the number of chars they have left before they reach the max of 500 - When the 500th character is typed, the label reads “0 characters remaining”, and no further characters can be typed into the text area
- If the user types the backspace button (
KeyEvent.VK_BACK_SPACE), they “free” up a character, and the count increments. Thus if they had 400 characters remaining, and they type backspace, the label now reads “401 characters remaining” - If the user highlights a set of characters and performs a bulk command on them (such as a backspace, or replacing the highlighted text with a single character), the correct # of chars remaining will be calculated correctly and the label will be updated. So if they have 50 chars remaining, and they highlight 5 letters and hit backspace, they now have “55 characters remaining”
I have 90% of this functionality working, but have a few bugs, and have no clue as to how to implement the last item above (bulk commands on highlighted text). Here’s what I have:
boolean ignoreInput = false;
int charMax = 500;
JLabel charCntLabel = getLabel();
JTextArea myTextArea = getTextArea();
myTextArea.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
return;
}
@Override
public void keyReleased(KeyEvent e) {
// If we should be ignoring input then set make sure we
// enforce max character count and remove the newly typed key.
if(ignoreInput)
myTextArea.setText(myTextArea.getText().substring(0,
myTextArea.getText().length()));
}
@Override
public void keyPressed(KeyEvent e) {
String charsRemaining = " characters remaining";
int newLen = 0;
// The key has just been pressed so Swing hasn't updated
// the text area with the new KeyEvent.
int currLen = myTextArea.getText().length();
// Adjust newLen depending on whether the user just pressed
// the backspace key or not.
if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
newLen = currLen - 1;
ignoreInput = false;
}
else
newLen = currLen + 1;
if(newLen < 0)
newLen = 0;
if(newLen == 0)
charCntLabel.setText(charMax + " characters maximum!");
else if(newLen >= 0 && newLen < charMax)
charCntLabel.setText((charMax - newLen) + charsRemaining);
else if(newLen >= charMax) {
ignoreInput = true;
charCntLabel.setText("0 " + charsRemaining);
}
}
});
The above code works pretty well, but has a few bugs:
- It doesn’t prevent the user from typing in > 500 characters. When the user types in the 500th character, the label reads “0 characters remaining.” But you can continue to type in characters after that, and the label stays the same.
- If you have > 500 characters in the textarea, and you start backspacing, you’ll see each character being removed from the textarea (the underlying model), but the label stays the same. But, once you backspace enough to get to the 500th character, and you backspace, the label will start changing properly, telling you that you have “1 characters remaining”, “2 characters remaining”, etc. So…
- This code seems to work but just stops working > 500 characters. Once you get back inside that 500 char max, it begins working again.
The questions
- Is there a simpler way to implement this desired functionality (and for a Swing JTextArea)? I feel like I’m reinventing the wheel here and that there might be a “cleaner” way of enforcing character maximums and updating their respective labels.
- If not, can anybody spot my > 500 char bug? I’ve been looking at it all morning and am pulling my hair out.
- Most importantly, how do I implement my requirement to handle bulk commands to highlighted text? How do I hand text selections inside the textarea, listen for changes to the highlighted text (e.g., deleting multiple highlighted characters with the backspace button, etc.), and correctly calculate the new value for chars remaining?
Thanks in advance.
You can limit the max size by using a DocumentFilter, check this documentation section, it has a working example of what you need.
Take this as an example, I used the component from the example file above: