Last active
September 8, 2021 18:49
-
-
Save DavidJCobb/1c8bdb862955f7ce9ceb5e1096a3af48 to your computer and use it in GitHub Desktop.
QPlainTextEdit maxlength implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// given: | |
QPlainTextEdit* widget; | |
int maxlength; | |
auto* doc = widget->document(); | |
assert(doc); | |
QObject::connect(doc, &QTextDocument::contentsChange, this, [maxlength, widget](int pos, int removed, int added) { | |
if (added - removed <= 0) | |
return; | |
if (maxlength < 0) | |
return; | |
auto text = widget->toPlainText(); | |
if (text.size() <= maxlength) | |
return; | |
// | |
auto* doc = widget->document(); | |
auto cur = QTextCursor(doc); | |
const auto blocker = QSignalBlocker(doc); | |
cur.setPosition(pos); | |
cur.setPosition(pos + added, QTextCursor::MoveMode::KeepAnchor); | |
if (cur.hasSelection()) { | |
cur.deleteChar(); | |
text = widget->toPlainText(); | |
} | |
if (text.size() > maxlength) { // failsafe | |
text = text.left(maxlength); | |
widget->setPlainText(text); | |
} | |
QApplication::beep(); | |
}); | |
// Other approaches (some seen on the web), and their flaws: | |
// EVENT FILTER: Hook the keyEvent and block input if already at the max length | |
// - Fails if the user pastes a multi-character string in. | |
// - seriously, this is so flimsy, why does anyone suggest it | |
// TRUNCATE: Hook QPlainTextEdit::textChanged and truncate the length | |
// - Intended to create the appearance of preventing input by instantly reverting it | |
// - Fails if the user types into the middle of the value: we truncate the end | |
// - Fails if the user pastes in a long string: we don't know how many characters to delete | |
// - Leveraging QPlainTextEdit::textCursor fixes the middle-typing issue but not the pasting issue | |
// UNDO: Use QPlainTextEdit::undo | |
// - Properly handles multi-character operations including pastes... | |
// - ...except for pressing and holding a key. | |
// - If you hold "A" until the max length is reached, all "A"s are deleted, not just the last. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's worth noting that I think this would double
textChanged
events. The QPlainTextEdit internals hook the same signals on QTextDocument, and signal/slot connections are handled in the order they're made, so by the time we have a chance to enforce the max length, I believetextChange
will have already been fired.My approach is to subclass QPlainTextEdit and use a variant on this function which emits a signal when input isn't rejected; essentially it's a duplicate of the
textChange
signal.