7#if QT_CONFIG(itemviews)
8#include "qabstractitemview.h"
11#include <private/qguiapplication_p.h>
12#if QT_CONFIG(completer)
13#include <private/qcompleter_p.h>
15#include <qpa/qplatformtheme.h>
16#include <qstylehints.h>
17#if QT_CONFIG(accessibility)
18#include "qaccessible.h"
22#include "private/qapplication_p.h"
23#if QT_CONFIG(graphicsview)
24#include "qgraphicssceneevent.h"
29using namespace std::chrono_literals;
35
36
37
38
39
40int QWidgetLineControl::redoTextLayout()
const
42 m_textLayout.clearLayout();
44 m_textLayout.beginLayout();
45 QTextLine l = m_textLayout.createLine();
46 m_textLayout.endLayout();
48 return qRound(l.ascent());
52
53
54
55
56
57void QWidgetLineControl::updateDisplayText(
bool forceUpdate)
59 QString orig = m_textLayout.text();
61 if (m_echoMode == QLineEdit::NoEcho)
62 str = QString::fromLatin1(
"");
66 if (m_echoMode == QLineEdit::Password) {
67 str.fill(m_passwordCharacter);
68 if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.size()) {
69 int cursor = m_cursor - 1;
70 QChar uc = m_text.at(cursor);
72 if (cursor > 0 && uc.isLowSurrogate()) {
75 uc = m_text.at(cursor - 1);
76 if (uc.isHighSurrogate())
80 }
else if (m_echoMode == QLineEdit::PasswordEchoOnEdit && !m_passwordEchoEditing) {
81 str.fill(m_passwordCharacter);
87 QChar* uc = str.data();
88 for (
int i = 0; i < (
int)str.size(); ++i) {
89 if ((uc[i].unicode() < 0x20 && uc[i].unicode() != 0x09)
90 || uc[i] == QChar::LineSeparator
91 || uc[i] == QChar::ParagraphSeparator)
92 uc[i] = QChar(0x0020);
95 m_textLayout.setText(str);
97 QTextOption option = m_textLayout.textOption();
98 option.setTextDirection(m_layoutDirection);
99 option.setFlags(QTextOption::IncludeTrailingSpaces);
100 m_textLayout.setTextOption(option);
102 m_ascent = redoTextLayout();
104 if (str != orig || forceUpdate)
105 emit displayTextChanged(str);
108#ifndef QT_NO_CLIPBOARD
110
111
112
113
114
115
116
117
118
119void QWidgetLineControl::copy(QClipboard::Mode mode)
const
121 QString t = selectedText();
122 if (!t.isEmpty() && m_echoMode == QLineEdit::Normal) {
123 QGuiApplication::clipboard()->setText(t, mode);
128
129
130
131
132
133
134
135void QWidgetLineControl::paste(QClipboard::Mode clipboardMode)
137 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
138 if (!clip.isEmpty() || hasSelectedText()) {
148
149
150void QWidgetLineControl::commitPreedit()
156 QGuiApplication::inputMethod()->commit();
161 setPreeditArea(-1, QString());
162 m_textLayout.clearFormats();
163 updateDisplayText(
true);
169
170
171
172
173
174
175
176
177void QWidgetLineControl::backspace()
179 int priorState = m_undoState;
180 if (hasSelectedText()) {
181 removeSelectedText();
182 }
else if (m_cursor) {
185 m_cursor = prevMaskBlank(m_cursor);
186 QChar uc = m_text.at(m_cursor);
187 if (m_cursor > 0 && uc.isLowSurrogate()) {
190 uc = m_text.at(m_cursor - 1);
191 if (uc.isHighSurrogate()) {
192 internalDelete(
true);
196 internalDelete(
true);
198 finishChange(priorState);
202
203
204
205
206
207
208
209
210void QWidgetLineControl::del()
212 int priorState = m_undoState;
213 if (hasSelectedText()) {
214 removeSelectedText();
216 int n = textLayout()->nextCursorPosition(m_cursor) - m_cursor;
220 finishChange(priorState);
224
225
226
227
228
229
230void QWidgetLineControl::insert(
const QString &newText)
232 int priorState = m_undoState;
233 removeSelectedText();
234 internalInsert(newText);
235 finishChange(priorState);
239
240
241
242
243void QWidgetLineControl::clear()
245 int priorState = m_undoState;
247 m_selend = m_text.size();
248 removeSelectedText();
250 finishChange(priorState,
false,
false);
253
254
255
256
258void QWidgetLineControl::undo()
261 if (m_echoMode == QLineEdit::Normal) {
263 finishChange(-1,
true);
265 cancelPasswordEchoTimer();
271
272
273
274
275
276
277
278void QWidgetLineControl::setSelection(
int start,
int length)
282 if (Q_UNLIKELY(start < 0 || start > m_text.size())) {
283 qWarning(
"QWidgetLineControl::setSelection: Invalid start position");
288 if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
291 m_selend = qMin(start + length, (
int)m_text.size());
293 }
else if (length < 0){
294 if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
296 m_selstart = qMax(start + length, 0);
298 m_cursor = m_selstart;
299 }
else if (m_selstart != m_selend) {
305 emitCursorPositionChanged();
308 emit selectionChanged();
309 emitCursorPositionChanged();
312void QWidgetLineControl::_q_deleteSelected()
314 if (!hasSelectedText())
317 int priorState = m_undoState;
318 emit resetInputContext();
319 removeSelectedText();
321 finishChange(priorState);
325
326
327
328
329void QWidgetLineControl::init(
const QString &txt)
331 m_textLayout.setCacheEnabled(
true);
334 m_cursor = m_text.size();
335 if (
const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
336 m_keyboardScheme = theme->themeHint(QPlatformTheme::KeyboardScheme).toInt();
337 m_passwordMaskDelay = theme->themeHint(QPlatformTheme::PasswordMaskDelay).toInt();
340 if (m_keyboardScheme == QPlatformTheme::KdeKeyboardScheme
341 || m_keyboardScheme == QPlatformTheme::GnomeKeyboardScheme
342 || m_keyboardScheme == QPlatformTheme::CdeKeyboardScheme) {
343 m_keyboardScheme = QPlatformTheme::X11KeyboardScheme;
348
349
350
351
352
353
354
355void QWidgetLineControl::updatePasswordEchoEditing(
bool editing)
357 cancelPasswordEchoTimer();
358 m_passwordEchoEditing = editing;
363
364
365
366
367
368
369int QWidgetLineControl::xToPos(
int x, QTextLine::CursorPosition betweenOrOn)
const
371 return textLayout()->lineAt(0).xToCursor(x, betweenOrOn);
375
376
377
378
379QRect QWidgetLineControl::rectForPos(
int pos)
const
381 QTextLine l = textLayout()->lineAt(0);
382 if (m_preeditCursor != -1)
383 pos += m_preeditCursor;
384 int cix = qRound(l.cursorToX(pos));
385 int w = m_cursorWidth;
386 int ch = l.height() + 1;
388 return QRect(cix - 5, 0, w + 9, ch);
392
393
394
395
396
397QRect QWidgetLineControl::cursorRect()
const
399 return rectForPos(m_cursor);
403
404
405
406
407QRect QWidgetLineControl::anchorRect()
const
409 if (!hasSelectedText())
411 return rectForPos(m_cursor == m_selstart ? m_selend : m_selstart);
415
416
417
418
419
420
421bool QWidgetLineControl::fixup()
423#ifndef QT_NO_VALIDATOR
425 QString textCopy = m_text;
426 int cursorCopy = m_cursor;
427 m_validator->fixup(textCopy);
428 if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
429 if (textCopy != m_text || cursorCopy != m_cursor)
430 internalSetText(textCopy, cursorCopy,
false);
439
440
441
442
443
444void QWidgetLineControl::moveCursor(
int pos,
bool mark)
448 if (pos != m_cursor) {
451 pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
455 if (m_selend > m_selstart && m_cursor == m_selstart)
457 else if (m_selend > m_selstart && m_cursor == m_selend)
461 m_selstart = qMin(anchor, pos);
462 m_selend = qMax(anchor, pos);
468 if (mark || m_selDirty) {
470 emit selectionChanged();
472 emitCursorPositionChanged();
476
477
478
479
480
481void QWidgetLineControl::processInputMethodEvent(QInputMethodEvent *event)
484 bool isGettingInput = !event->commitString().isEmpty()
485 || event->preeditString() != preeditAreaText()
486 || event->replacementLength() > 0;
487 bool cursorPositionChanged =
false;
488 bool selectionChange =
false;
490 if (isGettingInput) {
492 priorState = m_undoState;
493 if (echoMode() == QLineEdit::PasswordEchoOnEdit && !passwordEchoEditing()) {
494 updatePasswordEchoEditing(
true);
496 m_selend = m_text.size();
498 removeSelectedText();
502 if (event->replacementStart() <= 0)
503 c += event->commitString().size() - qMin(-event->replacementStart(), event->replacementLength());
505 m_cursor += event->replacementStart();
510 if (event->replacementLength()) {
511 m_selstart = m_cursor;
512 m_selend = m_selstart + event->replacementLength();
513 removeSelectedText();
515 if (!event->commitString().isEmpty()) {
516 internalInsert(event->commitString());
517 cursorPositionChanged =
true;
519 m_cursor = qBound(0, c, m_text.size());
522 for (
int i = 0; i < event->attributes().size(); ++i) {
523 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
524 if (a.type == QInputMethodEvent::Selection) {
525 m_cursor = qBound(0, a.start + a.length, m_text.size());
527 m_selstart = qMax(0, qMin(a.start, m_text.size()));
529 if (m_selend < m_selstart) {
530 qSwap(m_selstart, m_selend);
532 selectionChange =
true;
534 if (m_selstart != m_selend)
535 selectionChange =
true;
536 m_selstart = m_selend = 0;
538 cursorPositionChanged =
true;
543 switch (m_echoMode) {
544 case QLineEdit::NoEcho:
545 setPreeditArea(0, QString());
547 case QLineEdit::Password: {
548 QString preeditString = event->preeditString();
549 preeditString.fill(m_passwordCharacter);
550 setPreeditArea(m_cursor, preeditString);
554 setPreeditArea(m_cursor, event->preeditString());
558 const int oldPreeditCursor = m_preeditCursor;
559 m_preeditCursor = event->preeditString().size();
560 m_hideCursor =
false;
561 QList<QTextLayout::FormatRange> formats;
562 formats.reserve(event->attributes().size());
563 for (
int i = 0; i < event->attributes().size(); ++i) {
564 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
565 if (a.type == QInputMethodEvent::Cursor) {
566 m_preeditCursor = a.start;
567 m_hideCursor = !a.length;
568 }
else if (a.type == QInputMethodEvent::TextFormat) {
569 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
571 QTextLayout::FormatRange o;
572 o.start = a.start + m_cursor;
579 m_textLayout.setFormats(formats);
580 updateDisplayText(
true);
581 if (cursorPositionChanged)
582 emitCursorPositionChanged();
583 else if (m_preeditCursor != oldPreeditCursor)
584 emit updateMicroFocus();
587 finishChange(priorState);
590 emit selectionChanged();
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608void QWidgetLineControl::draw(QPainter *painter,
const QPoint &offset,
const QRect &clip,
int flags)
610 QList<QTextLayout::FormatRange> selections;
611 if (flags & DrawSelections) {
612 QTextLayout::FormatRange o;
613 if (m_selstart < m_selend) {
614 o.start = m_selstart;
615 o.length = m_selend - m_selstart;
616 o.format.setBackground(m_palette.brush(QPalette::Highlight));
617 o.format.setForeground(m_palette.brush(QPalette::HighlightedText));
623 o.format.setBackground(m_palette.brush(QPalette::Text));
624 o.format.setForeground(m_palette.brush(QPalette::Window));
627 selections.append(o);
630 if (flags & DrawText)
631 textLayout()->draw(painter, offset, selections, clip);
633 if (flags & DrawCursor){
634 int cursor = m_cursor;
635 if (m_preeditCursor != -1)
636 cursor += m_preeditCursor;
637 if (!m_hideCursor && m_blinkStatus)
638 textLayout()->drawCursor(painter, offset, cursor, m_cursorWidth);
643
644
645
646
647
648
649void QWidgetLineControl::selectWordAtPos(
int cursor)
651 int next = cursor + 1;
654 int c = textLayout()->previousCursorPosition(next, QTextLayout::SkipWords);
655 moveCursor(c,
false);
657 int end = textLayout()->nextCursorPosition(c, QTextLayout::SkipWords);
658 while (end > cursor && m_text[end-1].isSpace())
660 moveCursor(end,
true);
664
665
666
667
668
669
670
671
672
673
674
675bool QWidgetLineControl::finishChange(
int validateFromState,
bool update,
bool edited)
681 bool wasValidInput = m_validInput;
683#ifndef QT_NO_VALIDATOR
685 QString textCopy = m_text;
686 int cursorCopy = m_cursor;
687 m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
689 if (m_text != textCopy) {
690 internalSetText(textCopy, cursorCopy, edited);
693 m_cursor = cursorCopy;
695 emit inputRejected();
699 if (validateFromState >= 0 && wasValidInput && !m_validInput) {
700 if (m_transactions.size())
702 internalUndo(validateFromState);
703 m_history.erase(m_history.begin() + m_undoState, m_history.end());
704 if (m_modifiedState > m_undoState)
705 m_modifiedState = -1;
713 QString actualText = text();
715 emit textEdited(actualText);
716 emit textChanged(actualText);
721 emit selectionChanged();
723 if (m_cursor == m_lastCursorPos)
724 emit updateMicroFocus();
725 emitCursorPositionChanged();
730
731
732
733
734void QWidgetLineControl::internalSetText(
const QString &txt,
int pos,
bool edited)
736 cancelPasswordEchoTimer();
738 emit resetInputContext();
739 QString oldText = m_text;
741 m_text = maskString(0, txt,
true);
742 m_text += clearString(m_text.size(), m_maxLength - m_text.size());
743 if (edited && oldText == m_text)
744 emit inputRejected();
746 m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
749 m_modifiedState = m_undoState = 0;
750 m_cursor = (pos < 0 || pos > m_text.size()) ? m_text.size() : pos;
751 m_textDirty = (oldText != m_text);
752 const bool changed = finishChange(-1,
true, edited);
754#if QT_CONFIG(accessibility)
756 if (oldText.isEmpty()) {
757 QAccessibleTextInsertEvent event(accessibleObject(), 0, txt);
758 event.setCursorPosition(m_cursor);
759 QAccessible::updateAccessibility(&event);
760 }
else if (txt.isEmpty()) {
761 QAccessibleTextRemoveEvent event(accessibleObject(), 0, oldText);
762 event.setCursorPosition(m_cursor);
763 QAccessible::updateAccessibility(&event);
765 QAccessibleTextUpdateEvent event(accessibleObject(), 0, oldText, txt);
766 event.setCursorPosition(m_cursor);
767 QAccessible::updateAccessibility(&event);
777
778
779
780
781
782void QWidgetLineControl::addCommand(
const Command &cmd)
784 m_history.erase(m_history.begin() + m_undoState, m_history.end());
786 if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator)
787 m_history.push_back(Command(Separator, m_cursor, u'\0', m_selstart, m_selend));
790 m_history.push_back(cmd);
791 m_undoState =
int(m_history.size());
795
796
797
798
799
800
801
802
803
804void QWidgetLineControl::internalInsert(
const QString &s)
806 if (m_echoMode == QLineEdit::Password) {
807 if (m_passwordEchoTimer.isActive())
808 m_passwordEchoTimer.stop();
809 int delay = m_passwordMaskDelay;
810#ifdef QT_BUILD_INTERNAL
811 if (m_passwordMaskDelayOverride >= 0)
812 delay = m_passwordMaskDelayOverride;
816 m_passwordEchoTimer.start(delay * 1ms,
this);
818 if (hasSelectedText())
819 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
821 QString ms = maskString(m_cursor, s);
822 if (ms.isEmpty() && !s.isEmpty())
823 emit inputRejected();
824#if QT_CONFIG(accessibility)
825 QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, ms);
826 QAccessible::updateAccessibility(&insertEvent);
828 for (
int i = 0; i < (
int) ms.size(); ++i) {
829 addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
830 addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
832 m_text.replace(m_cursor, ms.size(), ms);
833 m_cursor += ms.size();
834 m_cursor = nextMaskBlank(m_cursor);
836#if QT_CONFIG(accessibility)
837 QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
838 QAccessible::updateAccessibility(&event);
841 int remaining = m_maxLength - m_text.size();
842 if (remaining != 0) {
843#if QT_CONFIG(accessibility)
844 QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, s);
845 QAccessible::updateAccessibility(&insertEvent);
847 m_text.insert(m_cursor, s.left(remaining));
848 for (
int i = 0; i < (
int) s.left(remaining).size(); ++i)
849 addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
852 if (s.size() > remaining)
853 emit inputRejected();
858
859
860
861
862
863
864
865
866
867
868void QWidgetLineControl::internalDelete(
bool wasBackspace)
870 if (m_cursor < (
int) m_text.size()) {
871 cancelPasswordEchoTimer();
872 if (hasSelectedText())
873 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
874 addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
875 m_cursor, m_text.at(m_cursor), -1, -1));
876#if QT_CONFIG(accessibility)
877 QAccessibleTextRemoveEvent event(accessibleObject(), m_cursor, m_text.at(m_cursor));
878 QAccessible::updateAccessibility(&event);
881 m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
882 addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
884 m_text.remove(m_cursor, 1);
891
892
893
894
895
896
897
898
899void QWidgetLineControl::removeSelectedText()
901 if (m_selstart < m_selend && m_selend <= (
int) m_text.size()) {
902 cancelPasswordEchoTimer();
905 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
906 if (m_selstart <= m_cursor && m_cursor < m_selend) {
909 for (i = m_cursor; i >= m_selstart; --i)
910 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
911 for (i = m_selend - 1; i > m_cursor; --i)
912 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
914 for (i = m_selend-1; i >= m_selstart; --i)
915 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
917#if QT_CONFIG(accessibility)
918 QAccessibleTextRemoveEvent event(accessibleObject(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart));
919 QAccessible::updateAccessibility(&event);
922 m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
923 for (
int i = 0; i < m_selend - m_selstart; ++i)
924 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
926 m_text.remove(m_selstart, m_selend - m_selstart);
928 if (m_cursor > m_selstart)
929 m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
936
937
938
939
940
941void QWidgetLineControl::parseInputMask(
const QString &maskFields)
943 qsizetype delimiter = maskFields.indexOf(u';');
944 if (maskFields.isEmpty() || delimiter == 0) {
948 internalSetText(QString(), -1,
false);
953 if (delimiter == -1) {
955 m_inputMask = maskFields;
957 m_inputMask = maskFields.left(delimiter);
958 m_blank = (delimiter + 1 < maskFields.size()) ? maskFields[delimiter + 1] : u' ';
963 bool escaped =
false;
964 for (
int i=0; i<m_inputMask.size(); i++) {
965 const auto c = m_inputMask.at(i);
977 if (c != u'\\' && c != u'!' && c != u'<' && c != u'>' &&
978 c != u'{' && c != u'}' && c != u'[' && c != u']')
982 m_maskData = std::make_unique<MaskInputData[]>(m_maxLength);
984 MaskInputData::Casemode m = MaskInputData::NoCaseMode;
988 for (
int i = 0; i < m_inputMask.size(); i++) {
989 const auto c = m_inputMask.at(i);
992 m_maskData[index].maskChar = c;
993 m_maskData[index].separator = s;
994 m_maskData[index].caseMode = m;
997 }
else if (c == u'<') {
998 m = MaskInputData::Lower;
999 }
else if (c == u'>') {
1000 m = MaskInputData::Upper;
1001 }
else if (c == u'!') {
1002 m = MaskInputData::NoCaseMode;
1003 }
else if (c != u'{' && c != u'}' && c != u'[' && c != u']') {
1004 switch (c.unicode()) {
1031 m_maskData[index].maskChar = c;
1032 m_maskData[index].separator = s;
1033 m_maskData[index].caseMode = m;
1038 internalSetText(m_text, -1,
false);
1043
1044
1045
1046
1047bool QWidgetLineControl::isValidInput(QChar key, QChar mask)
const
1049 switch (mask.unicode()) {
1055 if (key.isLetter() || key == m_blank)
1059 if (key.isLetterOrNumber())
1063 if (key.isLetterOrNumber() || key == m_blank)
1067 if (key.isPrint() && key != m_blank)
1071 if (key.isPrint() || key == m_blank)
1079 if (key.isNumber() || key == m_blank)
1083 if (key.isNumber() && key.digitValue() > 0)
1087 if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
1091 if (key.isNumber() || key == u'+' || key == u'-' || key == m_blank)
1095 if (key == u'0' || key == u'1')
1099 if (key == u'0' || key == u'1' || key == m_blank)
1103 if (key.isNumber() || (key >= u'a' && key <= u'f') || (key >= u'A' && key <= u'F'))
1107 if (key.isNumber() || (key >= u'a' && key <= u'f') || (key >= u'A' && key <= u'F') || key == m_blank)
1117
1118
1119
1120
1121
1122
1123
1124bool QWidgetLineControl::hasAcceptableInput(
const QString &str)
const
1126#ifndef QT_NO_VALIDATOR
1127 QString textCopy = str;
1128 int cursorCopy = m_cursor;
1129 if (m_validator && m_validator->validate(textCopy, cursorCopy)
1130 != QValidator::Acceptable)
1137 if (str.size() != m_maxLength)
1140 for (
int i=0; i < m_maxLength; ++i) {
1141 if (m_maskData[i].separator) {
1142 if (str.at(i) != m_maskData[i].maskChar)
1145 if (!isValidInput(str.at(i), m_maskData[i].maskChar))
1153
1154
1155
1156
1157
1158
1159
1160QString QWidgetLineControl::maskString(
int pos,
const QString &str,
bool clear)
const
1162 if (pos >= m_maxLength)
1163 return QString::fromLatin1(
"");
1166 fill = clear ? clearString(0, m_maxLength) : m_text;
1169 QString s = QString::fromLatin1(
"");
1171 while (i < m_maxLength) {
1172 if (strIndex < str.size()) {
1173 if (m_maskData[i].separator) {
1174 s += m_maskData[i].maskChar;
1175 if (str[strIndex] == m_maskData[i].maskChar)
1179 if (isValidInput(str[strIndex], m_maskData[i].maskChar)) {
1180 switch (m_maskData[i].caseMode) {
1181 case MaskInputData::Upper:
1182 s += str[strIndex].toUpper();
1184 case MaskInputData::Lower:
1185 s += str[strIndex].toLower();
1193 int n = findInMask(i,
true,
true, str[strIndex]);
1195 if (str.size() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[strIndex]))) {
1196 s += QStringView{fill}.mid(i, n - i + 1);
1201 n = findInMask(i,
true,
false, str[strIndex]);
1203 s += QStringView{fill}.mid(i, n - i);
1204 switch (m_maskData[n].caseMode) {
1205 case MaskInputData::Upper:
1206 s += str[strIndex].toUpper();
1208 case MaskInputData::Lower:
1209 s += str[strIndex].toLower();
1230
1231
1232
1233
1234
1235QString QWidgetLineControl::clearString(
int pos,
int len)
const
1237 if (pos >= m_maxLength)
1241 int end = qMin(m_maxLength, pos + len);
1242 for (
int i = pos; i < end; ++i)
1243 if (m_maskData[i].separator)
1244 s += m_maskData[i].maskChar;
1252
1253
1254
1255
1256
1257QString QWidgetLineControl::stripString(
const QString &str)
const
1263 int end = qMin(m_maxLength, (
int)str.size());
1264 for (
int i = 0; i < end; ++i)
1265 if (m_maskData[i].separator)
1266 s += m_maskData[i].maskChar;
1268 if (str[i] != m_blank)
1275
1276
1277
1278int QWidgetLineControl::findInMask(
int pos,
bool forward,
bool findSeparator, QChar searchChar)
const
1280 if (pos >= m_maxLength || pos < 0)
1283 int end = forward ? m_maxLength : -1;
1284 int step = forward ? 1 : -1;
1288 if (findSeparator) {
1289 if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
1292 if (!m_maskData[i].separator) {
1293 if (searchChar.isNull())
1295 else if (isValidInput(searchChar, m_maskData[i].maskChar))
1304void QWidgetLineControl::internalUndo(
int until)
1306 if (!isUndoAvailable())
1308 cancelPasswordEchoTimer();
1311 while (m_undoState && m_undoState > until) {
1312 Command& cmd = m_history[--m_undoState];
1315 m_text.remove(cmd.pos, 1);
1319 m_selstart = cmd.selStart;
1320 m_selend = cmd.selEnd;
1324 case RemoveSelection:
1325 m_text.insert(cmd.pos, cmd.uc);
1326 m_cursor = cmd.pos + 1;
1329 case DeleteSelection:
1330 m_text.insert(cmd.pos, cmd.uc);
1336 if (until < 0 && m_undoState) {
1337 Command& next = m_history[m_undoState-1];
1338 if (next.type != cmd.type && next.type < RemoveSelection
1339 && (cmd.type < RemoveSelection || next.type == Separator))
1344 emitCursorPositionChanged();
1347void QWidgetLineControl::internalRedo()
1349 if (!isRedoAvailable())
1352 while (m_undoState < (
int)m_history.size()) {
1353 Command& cmd = m_history[m_undoState++];
1356 m_text.insert(cmd.pos, cmd.uc);
1357 m_cursor = cmd.pos + 1;
1360 m_selstart = cmd.selStart;
1361 m_selend = cmd.selEnd;
1366 case RemoveSelection:
1367 case DeleteSelection:
1368 m_text.remove(cmd.pos, 1);
1369 m_selstart = cmd.selStart;
1370 m_selend = cmd.selEnd;
1374 m_selstart = cmd.selStart;
1375 m_selend = cmd.selEnd;
1379 if (m_undoState < (
int)m_history.size()) {
1380 Command& next = m_history[m_undoState];
1381 if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
1382 && (next.type < RemoveSelection || cmd.type == Separator))
1387 emitCursorPositionChanged();
1391
1392
1393
1394
1395
1396void QWidgetLineControl::emitCursorPositionChanged()
1398 if (m_cursor != m_lastCursorPos) {
1399 const int oldLast = m_lastCursorPos;
1400 m_lastCursorPos = m_cursor;
1401 emit cursorPositionChanged(oldLast, m_cursor);
1402#if QT_CONFIG(accessibility)
1404 if (!hasSelectedText()) {
1405 QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
1406 QAccessible::updateAccessibility(&event);
1412#if QT_CONFIG(completer)
1415bool QWidgetLineControl::advanceToEnabledItem(
int dir)
1417 int start = m_completer->currentRow();
1420 int i = start + dir;
1421 if (dir == 0) dir = 1;
1423 if (!m_completer->setCurrentRow(i)) {
1424 if (!m_completer->wrapAround())
1426 i = i > 0 ? 0 : m_completer->completionCount() - 1;
1428 QModelIndex currentIndex = m_completer->currentIndex();
1429 if (m_completer->completionModel()->flags(currentIndex) & Qt::ItemIsEnabled)
1433 }
while (i != start);
1435 m_completer->setCurrentRow(start);
1439void QWidgetLineControl::complete(
int key)
1441 if (!m_completer || isReadOnly() || echoMode() != QLineEdit::Normal)
1444 QString text =
this->text();
1445 if (m_completer->completionMode() == QCompleter::InlineCompletion) {
1446 if (key == Qt::Key_Backspace)
1449 if (key == Qt::Key_Up || key == Qt::Key_Down) {
1450 if (textAfterSelection().size())
1452 QString prefix = hasSelectedText() ? textBeforeSelection()
1454 if (text.compare(m_completer->currentCompletion(), m_completer->caseSensitivity()) != 0
1455 || prefix.compare(m_completer->completionPrefix(), m_completer->caseSensitivity()) != 0) {
1456 m_completer->setCompletionPrefix(prefix);
1458 n = (key == Qt::Key_Up) ? -1 : +1;
1461 m_completer->setCompletionPrefix(text);
1463 if (!advanceToEnabledItem(n))
1466#ifndef QT_KEYPAD_NAVIGATION
1467 if (text.isEmpty()) {
1468 if (
auto *popup = QCompleterPrivate::get(m_completer)->popup)
1473 m_completer->setCompletionPrefix(text);
1476 m_completer->complete();
1480void QWidgetLineControl::setReadOnly(
bool enable)
1482 if (m_readOnly == enable)
1485 m_readOnly = enable;
1486 updateCursorBlinking();
1489void QWidgetLineControl::setBlinkingCursorEnabled(
bool enable)
1491 if (m_blinkEnabled == enable)
1494 m_blinkEnabled = enable;
1497 connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged,
this, &QWidgetLineControl::updateCursorBlinking);
1499 disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged,
this, &QWidgetLineControl::updateCursorBlinking);
1501 updateCursorBlinking();
1504void QWidgetLineControl::updateCursorBlinking()
1506 m_blinkTimer.stop();
1508 if (m_blinkEnabled && !m_readOnly) {
1509 const auto flashTime = QGuiApplication::styleHints()->cursorFlashTime() * 1ms;
1510 if (flashTime >= 2ms)
1511 m_blinkTimer.start(flashTime / 2,
this);
1515 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1519void QWidgetLineControl::resetCursorBlinkTimer()
1521 if (!m_blinkEnabled || !m_blinkTimer.isActive())
1523 m_blinkTimer.stop();
1524 const auto flashTime = QGuiApplication::styleHints()->cursorFlashTime() * 1ms;
1525 if (flashTime >= 2ms)
1526 m_blinkTimer.start(flashTime / 2,
this);
1530void QWidgetLineControl::timerEvent(QTimerEvent *event)
1532 const auto eventId = event->id();
1533 if (eventId == m_blinkTimer.id()) {
1534 m_blinkStatus = !m_blinkStatus;
1535 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1536 }
else if (eventId == m_deleteAllTimer.id()) {
1537 m_deleteAllTimer.stop();
1539 }
else if (eventId == m_tripleClickTimer.id()) {
1540 m_tripleClickTimer.stop();
1541 }
else if (eventId == m_passwordEchoTimer.id()) {
1542 m_passwordEchoTimer.stop();
1543 updateDisplayText();
1547#ifndef QT_NO_SHORTCUT
1548void QWidgetLineControl::processShortcutOverrideEvent(QKeyEvent *ke)
1550 if (ke == QKeySequence::Copy
1551 || ke == QKeySequence::MoveToNextWord
1552 || ke == QKeySequence::MoveToPreviousWord
1553 || ke == QKeySequence::MoveToStartOfLine
1554 || ke == QKeySequence::MoveToEndOfLine
1555 || ke == QKeySequence::MoveToStartOfDocument
1556 || ke == QKeySequence::MoveToEndOfDocument
1557 || ke == QKeySequence::SelectNextWord
1558 || ke == QKeySequence::SelectPreviousWord
1559 || ke == QKeySequence::SelectStartOfLine
1560 || ke == QKeySequence::SelectEndOfLine
1561 || ke == QKeySequence::SelectStartOfBlock
1562 || ke == QKeySequence::SelectEndOfBlock
1563 || ke == QKeySequence::SelectStartOfDocument
1564 || ke == QKeySequence::SelectAll
1565 || ke == QKeySequence::SelectEndOfDocument) {
1567 }
else if (ke == QKeySequence::Paste
1568 || ke == QKeySequence::Cut
1569 || ke == QKeySequence::Redo
1570 || ke == QKeySequence::Undo
1571 || ke == QKeySequence::DeleteCompleteLine) {
1574 }
else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1575 || ke->modifiers() == Qt::KeypadModifier) {
1576 if (ke->key() < Qt::Key_Escape) {
1580 switch (ke->key()) {
1581 case Qt::Key_Delete:
1582 case Qt::Key_Backspace:
1602void QWidgetLineControl::processKeyEvent(QKeyEvent* event)
1604 bool inlineCompletionAccepted =
false;
1606#if QT_CONFIG(completer)
1608 QCompleter::CompletionMode completionMode = m_completer->completionMode();
1609 auto *popup = QCompleterPrivate::get(m_completer)->popup;
1610 if ((completionMode == QCompleter::PopupCompletion
1611 || completionMode == QCompleter::UnfilteredPopupCompletion)
1612 && popup && popup->isVisible()) {
1615 switch (event->key()) {
1616 case Qt::Key_Escape:
1622 }
else if (completionMode == QCompleter::InlineCompletion) {
1623 switch (event->key()) {
1625 case Qt::Key_Return:
1627#ifdef QT_KEYPAD_NAVIGATION
1628 case Qt::Key_Select:
1629 if (!QApplicationPrivate::keypadNavigationEnabled())
1632 if (!m_completer->currentCompletion().isEmpty() && hasSelectedText()
1633 && !m_completer->completionPrefix().isEmpty()
1634 && textAfterSelection().isEmpty()) {
1635 setText(m_completer->currentCompletion());
1636 inlineCompletionAccepted =
true;
1646 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
1647 if (hasAcceptableInput() || fixup()) {
1649 QInputMethod *inputMethod = QGuiApplication::inputMethod();
1650 inputMethod->commit();
1651 QWidget *lineEdit = qobject_cast<QWidget *>(parent());
1652 if (!(lineEdit && lineEdit->inputMethodHints() & Qt::ImhMultiLine))
1653 inputMethod->hide();
1656 emit editingFinished();
1658 if (inlineCompletionAccepted)
1665 if (echoMode() == QLineEdit::PasswordEchoOnEdit
1666 && !passwordEchoEditing()
1668 && !event->text().isEmpty()
1669#ifdef QT_KEYPAD_NAVIGATION
1670 && event->key() != Qt::Key_Select
1671 && event->key() != Qt::Key_Up
1672 && event->key() != Qt::Key_Down
1673 && event->key() != Qt::Key_Back
1675 && !(event->modifiers() & Qt::ControlModifier)) {
1681 updatePasswordEchoEditing(
true);
1685 bool unknown =
false;
1686#if QT_CONFIG(shortcut)
1687 bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
1692#ifndef QT_NO_SHORTCUT
1693 else if (event == QKeySequence::Undo) {
1697 else if (event == QKeySequence::Redo) {
1701 else if (event == QKeySequence::SelectAll) {
1704#ifndef QT_NO_CLIPBOARD
1705 else if (event == QKeySequence::Copy) {
1708 else if (event == QKeySequence::Paste) {
1709 if (!isReadOnly()) {
1710 QClipboard::Mode mode = QClipboard::Clipboard;
1711 if (m_keyboardScheme == QPlatformTheme::X11KeyboardScheme
1712 && event->modifiers() == (Qt::CTRL | Qt::SHIFT)
1713 && event->key() == Qt::Key_Insert) {
1714 mode = QClipboard::Selection;
1719 else if (event == QKeySequence::Cut) {
1720 if (!isReadOnly() && hasSelectedText()) {
1725 else if (event == QKeySequence::DeleteEndOfLine) {
1726 if (!isReadOnly()) {
1727 setSelection(cursor(), end());
1733 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
1736 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
1739 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
1742 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
1745 else if (event == QKeySequence::MoveToNextChar) {
1746#if !QT_CONFIG(completer)
1747 const bool inlineCompletion =
false;
1749 const bool inlineCompletion = m_completer && m_completer->completionMode() == QCompleter::InlineCompletion;
1751 if (hasSelectedText()
1752 && (m_keyboardScheme != QPlatformTheme::WindowsKeyboardScheme
1753 || inlineCompletion)) {
1754 moveCursor(selectionEnd(),
false);
1756 cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1759 else if (event == QKeySequence::SelectNextChar) {
1760 cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1762 else if (event == QKeySequence::MoveToPreviousChar) {
1763#if !QT_CONFIG(completer)
1764 const bool inlineCompletion =
false;
1766 const bool inlineCompletion = m_completer && m_completer->completionMode() == QCompleter::InlineCompletion;
1768 if (hasSelectedText()
1769 && (m_keyboardScheme != QPlatformTheme::WindowsKeyboardScheme
1770 || inlineCompletion)) {
1771 moveCursor(selectionStart(),
false);
1773 cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1776 else if (event == QKeySequence::SelectPreviousChar) {
1777 cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1779 else if (event == QKeySequence::MoveToNextWord) {
1780 if (echoMode() == QLineEdit::Normal)
1781 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
1783 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
1785 else if (event == QKeySequence::MoveToPreviousWord) {
1786 if (echoMode() == QLineEdit::Normal)
1787 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
1788 else if (!isReadOnly()) {
1789 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
1792 else if (event == QKeySequence::SelectNextWord) {
1793 if (echoMode() == QLineEdit::Normal)
1794 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
1796 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
1798 else if (event == QKeySequence::SelectPreviousWord) {
1799 if (echoMode() == QLineEdit::Normal)
1800 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
1802 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
1804 else if (event == QKeySequence::Delete) {
1808 else if (event == QKeySequence::DeleteEndOfWord) {
1809 if (!isReadOnly()) {
1810 if (!hasSelectedText())
1811 cursorWordForward(
true);
1813 if (hasSelectedText())
1817 else if (event == QKeySequence::DeleteStartOfWord) {
1818 if (!isReadOnly()) {
1819 if (!hasSelectedText())
1820 cursorWordBackward(
true);
1822 if (hasSelectedText())
1825 }
else if (event == QKeySequence::DeleteCompleteLine) {
1826 if (!isReadOnly()) {
1827 setSelection(0, text().size());
1828#ifndef QT_NO_CLIPBOARD
1836 bool handled =
false;
1837 if (m_keyboardScheme == QPlatformTheme::MacKeyboardScheme
1838 && (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)) {
1839 Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier);
1840 if (myModifiers & Qt::ShiftModifier) {
1841 if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier)
1842 || myModifiers == (Qt::AltModifier|Qt::ShiftModifier)
1843 || myModifiers == Qt::ShiftModifier) {
1845 event->key() == Qt::Key_Up ? home(1) : end(1);
1848 if ((myModifiers == Qt::ControlModifier
1849 || myModifiers == Qt::AltModifier
1850 || myModifiers == Qt::NoModifier)) {
1851 event->key() == Qt::Key_Up ? home(0) : end(0);
1856 if (event->modifiers() & Qt::ControlModifier) {
1857 switch (event->key()) {
1858 case Qt::Key_Backspace:
1859 if (!isReadOnly()) {
1860 cursorWordBackward(
true);
1864#if QT_CONFIG(completer)
1867 complete(event->key());
1875 switch (event->key()) {
1876 case Qt::Key_Backspace:
1877 if (!isReadOnly()) {
1879#if QT_CONFIG(completer)
1880 complete(Qt::Key_Backspace);
1884#ifdef QT_KEYPAD_NAVIGATION
1886 if (QApplicationPrivate::keypadNavigationEnabled() && !event->isAutoRepeat()
1888 if (text().length() == 0) {
1889 setText(m_cancelText);
1891 if (passwordEchoEditing())
1892 updatePasswordEchoEditing(
false);
1894 emit editFocusChange(
false);
1895 }
else if (!m_deleteAllTimer.isActive()) {
1896 m_deleteAllTimer.start(750ms,
this);
1910 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
1911 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1917 && isAcceptableInput(event)) {
1918 insert(event->text());
1919#if QT_CONFIG(completer)
1920 complete(event->key());
1929#ifndef QT_NO_CLIPBOARD
1930 if (QApplication::clipboard()->supportsSelection())
1931 copy(QClipboard::Selection);
1937bool QWidgetLineControl::isUndoAvailable()
const
1941 return !m_readOnly && m_undoState
1942 && (m_echoMode == QLineEdit::Normal || m_history[m_undoState - 1].type == QWidgetLineControl::Insert);
1945bool QWidgetLineControl::isRedoAvailable()
const
1949 && m_echoMode == QLineEdit::Normal
1950 && m_undoState <
int(m_history.size());
1955#include "moc_qwidgetlinecontrol_p.cpp"