6#if QT_CONFIG(itemviews)
7#include "qabstractitemview.h"
10#include <private/qguiapplication_p.h>
11#if QT_CONFIG(completer)
12#include <private/qcompleter_p.h>
14#include <qpa/qplatformtheme.h>
15#include <qstylehints.h>
16#if QT_CONFIG(accessibility)
17#include "qaccessible.h"
21#include "private/qapplication_p.h"
22#if QT_CONFIG(graphicsview)
23#include "qgraphicssceneevent.h"
28using namespace std::chrono_literals;
34
35
36
37
38
39int QWidgetLineControl::redoTextLayout()
const
41 m_textLayout.clearLayout();
43 m_textLayout.beginLayout();
44 QTextLine l = m_textLayout.createLine();
45 m_textLayout.endLayout();
47 return qRound(l.ascent());
51
52
53
54
55
56void QWidgetLineControl::updateDisplayText(
bool forceUpdate)
58 QString orig = m_textLayout.text();
60 if (m_echoMode == QLineEdit::NoEcho)
61 str = QString::fromLatin1(
"");
65 if (m_echoMode == QLineEdit::Password) {
66 str.fill(m_passwordCharacter);
67 if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.size()) {
68 int cursor = m_cursor - 1;
69 QChar uc = m_text.at(cursor);
71 if (cursor > 0 && uc.isLowSurrogate()) {
74 uc = m_text.at(cursor - 1);
75 if (uc.isHighSurrogate())
79 }
else if (m_echoMode == QLineEdit::PasswordEchoOnEdit && !m_passwordEchoEditing) {
80 str.fill(m_passwordCharacter);
86 QChar* uc = str.data();
87 for (
int i = 0; i < (
int)str.size(); ++i) {
88 if ((uc[i].unicode() < 0x20 && uc[i].unicode() != 0x09)
89 || uc[i] == QChar::LineSeparator
90 || uc[i] == QChar::ParagraphSeparator)
91 uc[i] = QChar(0x0020);
94 m_textLayout.setText(str);
96 QTextOption option = m_textLayout.textOption();
97 option.setTextDirection(m_layoutDirection);
98 option.setFlags(QTextOption::IncludeTrailingSpaces);
99 m_textLayout.setTextOption(option);
101 m_ascent = redoTextLayout();
103 if (str != orig || forceUpdate)
104 emit displayTextChanged(str);
107#ifndef QT_NO_CLIPBOARD
109
110
111
112
113
114
115
116
117
118void QWidgetLineControl::copy(QClipboard::Mode mode)
const
120 QString t = selectedText();
121 if (!t.isEmpty() && m_echoMode == QLineEdit::Normal) {
122 QGuiApplication::clipboard()->setText(t, mode);
127
128
129
130
131
132
133
134void QWidgetLineControl::paste(QClipboard::Mode clipboardMode)
136 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
137 if (!clip.isEmpty() || hasSelectedText()) {
147
148
149void QWidgetLineControl::commitPreedit()
155 QGuiApplication::inputMethod()->commit();
160 setPreeditArea(-1, QString());
161 m_textLayout.clearFormats();
162 updateDisplayText(
true);
168
169
170
171
172
173
174
175
176void QWidgetLineControl::backspace()
178 int priorState = m_undoState;
179 if (hasSelectedText()) {
180 removeSelectedText();
181 }
else if (m_cursor) {
184 m_cursor = prevMaskBlank(m_cursor);
185 QChar uc = m_text.at(m_cursor);
186 if (m_cursor > 0 && uc.isLowSurrogate()) {
189 uc = m_text.at(m_cursor - 1);
190 if (uc.isHighSurrogate()) {
191 internalDelete(
true);
195 internalDelete(
true);
197 finishChange(priorState);
201
202
203
204
205
206
207
208
209void QWidgetLineControl::del()
211 int priorState = m_undoState;
212 if (hasSelectedText()) {
213 removeSelectedText();
215 int n = textLayout()->nextCursorPosition(m_cursor) - m_cursor;
219 finishChange(priorState);
223
224
225
226
227
228
229void QWidgetLineControl::insert(
const QString &newText)
231 int priorState = m_undoState;
232 removeSelectedText();
233 internalInsert(newText);
234 finishChange(priorState);
238
239
240
241
242void QWidgetLineControl::clear()
244 int priorState = m_undoState;
246 m_selend = m_text.size();
247 removeSelectedText();
249 finishChange(priorState,
false,
false);
252
253
254
255
257void QWidgetLineControl::undo()
260 if (m_echoMode == QLineEdit::Normal) {
262 finishChange(-1,
true);
264 cancelPasswordEchoTimer();
270
271
272
273
274
275
276
277void QWidgetLineControl::setSelection(
int start,
int length)
281 if (Q_UNLIKELY(start < 0 || start > m_text.size())) {
282 qWarning(
"QWidgetLineControl::setSelection: Invalid start position");
287 if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
290 m_selend = qMin(start + length, (
int)m_text.size());
292 }
else if (length < 0){
293 if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
295 m_selstart = qMax(start + length, 0);
297 m_cursor = m_selstart;
298 }
else if (m_selstart != m_selend) {
304 emitCursorPositionChanged();
307 emit selectionChanged();
308 emitCursorPositionChanged();
311void QWidgetLineControl::_q_deleteSelected()
313 if (!hasSelectedText())
316 int priorState = m_undoState;
317 emit resetInputContext();
318 removeSelectedText();
320 finishChange(priorState);
324
325
326
327
328void QWidgetLineControl::init(
const QString &txt)
330 m_textLayout.setCacheEnabled(
true);
333 m_cursor = m_text.size();
334 if (
const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
335 m_keyboardScheme = theme->themeHint(QPlatformTheme::KeyboardScheme).toInt();
336 m_passwordMaskDelay = theme->themeHint(QPlatformTheme::PasswordMaskDelay).toInt();
339 if (m_keyboardScheme == QPlatformTheme::KdeKeyboardScheme
340 || m_keyboardScheme == QPlatformTheme::GnomeKeyboardScheme
341 || m_keyboardScheme == QPlatformTheme::CdeKeyboardScheme) {
342 m_keyboardScheme = QPlatformTheme::X11KeyboardScheme;
347
348
349
350
351
352
353
354void QWidgetLineControl::updatePasswordEchoEditing(
bool editing)
356 cancelPasswordEchoTimer();
357 m_passwordEchoEditing = editing;
362
363
364
365
366
367
368int QWidgetLineControl::xToPos(
int x, QTextLine::CursorPosition betweenOrOn)
const
370 return textLayout()->lineAt(0).xToCursor(x, betweenOrOn);
374
375
376
377
378QRect QWidgetLineControl::rectForPos(
int pos)
const
380 QTextLine l = textLayout()->lineAt(0);
381 if (m_preeditCursor != -1)
382 pos += m_preeditCursor;
383 int cix = qRound(l.cursorToX(pos));
384 int w = m_cursorWidth;
385 int ch = l.height() + 1;
387 return QRect(cix - 5, 0, w + 9, ch);
391
392
393
394
395
396QRect QWidgetLineControl::cursorRect()
const
398 return rectForPos(m_cursor);
402
403
404
405
406QRect QWidgetLineControl::anchorRect()
const
408 if (!hasSelectedText())
410 return rectForPos(m_cursor == m_selstart ? m_selend : m_selstart);
414
415
416
417
418
419
420bool QWidgetLineControl::fixup()
422#ifndef QT_NO_VALIDATOR
424 QString textCopy = m_text;
425 int cursorCopy = m_cursor;
426 m_validator->fixup(textCopy);
427 if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
428 if (textCopy != m_text || cursorCopy != m_cursor)
429 internalSetText(textCopy, cursorCopy,
false);
438
439
440
441
442
443void QWidgetLineControl::moveCursor(
int pos,
bool mark)
447 if (pos != m_cursor) {
450 pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
454 if (m_selend > m_selstart && m_cursor == m_selstart)
456 else if (m_selend > m_selstart && m_cursor == m_selend)
460 m_selstart = qMin(anchor, pos);
461 m_selend = qMax(anchor, pos);
467 if (mark || m_selDirty) {
469 emit selectionChanged();
471 emitCursorPositionChanged();
475
476
477
478
479
480void QWidgetLineControl::processInputMethodEvent(QInputMethodEvent *event)
483 bool isGettingInput = !event->commitString().isEmpty()
484 || event->preeditString() != preeditAreaText()
485 || event->replacementLength() > 0;
486 bool cursorPositionChanged =
false;
487 bool selectionChange =
false;
489 if (isGettingInput) {
491 priorState = m_undoState;
492 if (echoMode() == QLineEdit::PasswordEchoOnEdit && !passwordEchoEditing()) {
493 updatePasswordEchoEditing(
true);
495 m_selend = m_text.size();
497 removeSelectedText();
501 if (event->replacementStart() <= 0)
502 c += event->commitString().size() - qMin(-event->replacementStart(), event->replacementLength());
504 m_cursor += event->replacementStart();
509 if (event->replacementLength()) {
510 m_selstart = m_cursor;
511 m_selend = m_selstart + event->replacementLength();
512 removeSelectedText();
514 if (!event->commitString().isEmpty()) {
515 internalInsert(event->commitString());
516 cursorPositionChanged =
true;
518 m_cursor = qBound(0, c, m_text.size());
521 for (
int i = 0; i < event->attributes().size(); ++i) {
522 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
523 if (a.type == QInputMethodEvent::Selection) {
524 m_cursor = qBound(0, a.start + a.length, m_text.size());
526 m_selstart = qMax(0, qMin(a.start, m_text.size()));
528 if (m_selend < m_selstart) {
529 qSwap(m_selstart, m_selend);
531 selectionChange =
true;
533 if (m_selstart != m_selend)
534 selectionChange =
true;
535 m_selstart = m_selend = 0;
537 cursorPositionChanged =
true;
542 switch (m_echoMode) {
543 case QLineEdit::NoEcho:
544 setPreeditArea(0, QString());
546 case QLineEdit::Password: {
547 QString preeditString = event->preeditString();
548 preeditString.fill(m_passwordCharacter);
549 setPreeditArea(m_cursor, preeditString);
553 setPreeditArea(m_cursor, event->preeditString());
557 const int oldPreeditCursor = m_preeditCursor;
558 m_preeditCursor = event->preeditString().size();
559 m_hideCursor =
false;
560 QList<QTextLayout::FormatRange> formats;
561 formats.reserve(event->attributes().size());
562 for (
int i = 0; i < event->attributes().size(); ++i) {
563 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
564 if (a.type == QInputMethodEvent::Cursor) {
565 m_preeditCursor = a.start;
566 m_hideCursor = !a.length;
567 }
else if (a.type == QInputMethodEvent::TextFormat) {
568 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
570 QTextLayout::FormatRange o;
571 o.start = a.start + m_cursor;
578 m_textLayout.setFormats(formats);
579 updateDisplayText(
true);
580 if (cursorPositionChanged)
581 emitCursorPositionChanged();
582 else if (m_preeditCursor != oldPreeditCursor)
583 emit updateMicroFocus();
586 finishChange(priorState);
589 emit selectionChanged();
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607void QWidgetLineControl::draw(QPainter *painter,
const QPoint &offset,
const QRect &clip,
int flags)
609 QList<QTextLayout::FormatRange> selections;
610 if (flags & DrawSelections) {
611 QTextLayout::FormatRange o;
612 if (m_selstart < m_selend) {
613 o.start = m_selstart;
614 o.length = m_selend - m_selstart;
615 o.format.setBackground(m_palette.brush(QPalette::Highlight));
616 o.format.setForeground(m_palette.brush(QPalette::HighlightedText));
622 o.format.setBackground(m_palette.brush(QPalette::Text));
623 o.format.setForeground(m_palette.brush(QPalette::Window));
626 selections.append(o);
629 if (flags & DrawText)
630 textLayout()->draw(painter, offset, selections, clip);
632 if (flags & DrawCursor){
633 int cursor = m_cursor;
634 if (m_preeditCursor != -1)
635 cursor += m_preeditCursor;
636 if (!m_hideCursor && m_blinkStatus)
637 textLayout()->drawCursor(painter, offset, cursor, m_cursorWidth);
642
643
644
645
646
647
648void QWidgetLineControl::selectWordAtPos(
int cursor)
650 int next = cursor + 1;
653 int c = textLayout()->previousCursorPosition(next, QTextLayout::SkipWords);
654 moveCursor(c,
false);
656 int end = textLayout()->nextCursorPosition(c, QTextLayout::SkipWords);
657 while (end > cursor && m_text[end-1].isSpace())
659 moveCursor(end,
true);
663
664
665
666
667
668
669
670
671
672
673
674bool QWidgetLineControl::finishChange(
int validateFromState,
bool update,
bool edited)
680 bool wasValidInput = m_validInput;
682#ifndef QT_NO_VALIDATOR
684 QString textCopy = m_text;
685 int cursorCopy = m_cursor;
686 m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
688 if (m_text != textCopy) {
689 internalSetText(textCopy, cursorCopy, edited);
692 m_cursor = cursorCopy;
694 emit inputRejected();
698 if (validateFromState >= 0 && wasValidInput && !m_validInput) {
699 if (m_transactions.size())
701 internalUndo(validateFromState);
702 m_history.erase(m_history.begin() + m_undoState, m_history.end());
703 if (m_modifiedState > m_undoState)
704 m_modifiedState = -1;
712 QString actualText = text();
714 emit textEdited(actualText);
715 emit textChanged(actualText);
720 emit selectionChanged();
722 if (m_cursor == m_lastCursorPos)
723 emit updateMicroFocus();
724 emitCursorPositionChanged();
729
730
731
732
733void QWidgetLineControl::internalSetText(
const QString &txt,
int pos,
bool edited)
735 cancelPasswordEchoTimer();
737 emit resetInputContext();
738 QString oldText = m_text;
740 m_text = maskString(0, txt,
true);
741 m_text += clearString(m_text.size(), m_maxLength - m_text.size());
742 if (edited && oldText == m_text)
743 emit inputRejected();
745 m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
748 m_modifiedState = m_undoState = 0;
749 m_cursor = (pos < 0 || pos > m_text.size()) ? m_text.size() : pos;
750 m_textDirty = (oldText != m_text);
751 const bool changed = finishChange(-1,
true, edited);
753#if QT_CONFIG(accessibility)
755 if (oldText.isEmpty()) {
756 QAccessibleTextInsertEvent event(accessibleObject(), 0, txt);
757 event.setCursorPosition(m_cursor);
758 QAccessible::updateAccessibility(&event);
759 }
else if (txt.isEmpty()) {
760 QAccessibleTextRemoveEvent event(accessibleObject(), 0, oldText);
761 event.setCursorPosition(m_cursor);
762 QAccessible::updateAccessibility(&event);
764 QAccessibleTextUpdateEvent event(accessibleObject(), 0, oldText, txt);
765 event.setCursorPosition(m_cursor);
766 QAccessible::updateAccessibility(&event);
776
777
778
779
780
781void QWidgetLineControl::addCommand(
const Command &cmd)
783 m_history.erase(m_history.begin() + m_undoState, m_history.end());
785 if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator)
786 m_history.push_back(Command(Separator, m_cursor, u'\0', m_selstart, m_selend));
789 m_history.push_back(cmd);
790 m_undoState =
int(m_history.size());
794
795
796
797
798
799
800
801
802
803void QWidgetLineControl::internalInsert(
const QString &s)
805 if (m_echoMode == QLineEdit::Password) {
806 if (m_passwordEchoTimer.isActive())
807 m_passwordEchoTimer.stop();
808 int delay = m_passwordMaskDelay;
809#ifdef QT_BUILD_INTERNAL
810 if (m_passwordMaskDelayOverride >= 0)
811 delay = m_passwordMaskDelayOverride;
815 m_passwordEchoTimer.start(delay * 1ms,
this);
817 if (hasSelectedText())
818 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
820 QString ms = maskString(m_cursor, s);
821 if (ms.isEmpty() && !s.isEmpty())
822 emit inputRejected();
823#if QT_CONFIG(accessibility)
824 QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, ms);
825 QAccessible::updateAccessibility(&insertEvent);
827 for (
int i = 0; i < (
int) ms.size(); ++i) {
828 addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
829 addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
831 m_text.replace(m_cursor, ms.size(), ms);
832 m_cursor += ms.size();
833 m_cursor = nextMaskBlank(m_cursor);
835#if QT_CONFIG(accessibility)
836 QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
837 QAccessible::updateAccessibility(&event);
840 int remaining = m_maxLength - m_text.size();
841 if (remaining != 0) {
842#if QT_CONFIG(accessibility)
843 QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, s);
844 QAccessible::updateAccessibility(&insertEvent);
846 m_text.insert(m_cursor, s.left(remaining));
847 for (
int i = 0; i < (
int) s.left(remaining).size(); ++i)
848 addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
851 if (s.size() > remaining)
852 emit inputRejected();
857
858
859
860
861
862
863
864
865
866
867void QWidgetLineControl::internalDelete(
bool wasBackspace)
869 if (m_cursor < (
int) m_text.size()) {
870 cancelPasswordEchoTimer();
871 if (hasSelectedText())
872 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
873 addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
874 m_cursor, m_text.at(m_cursor), -1, -1));
875#if QT_CONFIG(accessibility)
876 QAccessibleTextRemoveEvent event(accessibleObject(), m_cursor, m_text.at(m_cursor));
877 QAccessible::updateAccessibility(&event);
880 m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
881 addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
883 m_text.remove(m_cursor, 1);
890
891
892
893
894
895
896
897
898void QWidgetLineControl::removeSelectedText()
900 if (m_selstart < m_selend && m_selend <= (
int) m_text.size()) {
901 cancelPasswordEchoTimer();
904 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
905 if (m_selstart <= m_cursor && m_cursor < m_selend) {
908 for (i = m_cursor; i >= m_selstart; --i)
909 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
910 for (i = m_selend - 1; i > m_cursor; --i)
911 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
913 for (i = m_selend-1; i >= m_selstart; --i)
914 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
916#if QT_CONFIG(accessibility)
917 QAccessibleTextRemoveEvent event(accessibleObject(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart));
918 QAccessible::updateAccessibility(&event);
921 m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
922 for (
int i = 0; i < m_selend - m_selstart; ++i)
923 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
925 m_text.remove(m_selstart, m_selend - m_selstart);
927 if (m_cursor > m_selstart)
928 m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
935
936
937
938
939
940void QWidgetLineControl::parseInputMask(
const QString &maskFields)
942 qsizetype delimiter = maskFields.indexOf(u';');
943 if (maskFields.isEmpty() || delimiter == 0) {
947 internalSetText(QString(), -1,
false);
952 if (delimiter == -1) {
954 m_inputMask = maskFields;
956 m_inputMask = maskFields.left(delimiter);
957 m_blank = (delimiter + 1 < maskFields.size()) ? maskFields[delimiter + 1] : u' ';
962 bool escaped =
false;
963 for (
int i=0; i<m_inputMask.size(); i++) {
964 const auto c = m_inputMask.at(i);
976 if (c != u'\\' && c != u'!' && c != u'<' && c != u'>' &&
977 c != u'{' && c != u'}' && c != u'[' && c != u']')
981 m_maskData = std::make_unique<MaskInputData[]>(m_maxLength);
983 MaskInputData::Casemode m = MaskInputData::NoCaseMode;
987 for (
int i = 0; i < m_inputMask.size(); i++) {
988 const auto c = m_inputMask.at(i);
991 m_maskData[index].maskChar = c;
992 m_maskData[index].separator = s;
993 m_maskData[index].caseMode = m;
996 }
else if (c == u'<') {
997 m = MaskInputData::Lower;
998 }
else if (c == u'>') {
999 m = MaskInputData::Upper;
1000 }
else if (c == u'!') {
1001 m = MaskInputData::NoCaseMode;
1002 }
else if (c != u'{' && c != u'}' && c != u'[' && c != u']') {
1003 switch (c.unicode()) {
1030 m_maskData[index].maskChar = c;
1031 m_maskData[index].separator = s;
1032 m_maskData[index].caseMode = m;
1037 internalSetText(m_text, -1,
false);
1042
1043
1044
1045
1046bool QWidgetLineControl::isValidInput(QChar key, QChar mask)
const
1048 switch (mask.unicode()) {
1054 if (key.isLetter() || key == m_blank)
1058 if (key.isLetterOrNumber())
1062 if (key.isLetterOrNumber() || key == m_blank)
1066 if (key.isPrint() && key != m_blank)
1070 if (key.isPrint() || key == m_blank)
1078 if (key.isNumber() || key == m_blank)
1082 if (key.isNumber() && key.digitValue() > 0)
1086 if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
1090 if (key.isNumber() || key == u'+' || key == u'-' || key == m_blank)
1094 if (key == u'0' || key == u'1')
1098 if (key == u'0' || key == u'1' || key == m_blank)
1102 if (key.isNumber() || (key >= u'a' && key <= u'f') || (key >= u'A' && key <= u'F'))
1106 if (key.isNumber() || (key >= u'a' && key <= u'f') || (key >= u'A' && key <= u'F') || key == m_blank)
1116
1117
1118
1119
1120
1121
1122
1123bool QWidgetLineControl::hasAcceptableInput(
const QString &str)
const
1125#ifndef QT_NO_VALIDATOR
1126 QString textCopy = str;
1127 int cursorCopy = m_cursor;
1128 if (m_validator && m_validator->validate(textCopy, cursorCopy)
1129 != QValidator::Acceptable)
1136 if (str.size() != m_maxLength)
1139 for (
int i=0; i < m_maxLength; ++i) {
1140 if (m_maskData[i].separator) {
1141 if (str.at(i) != m_maskData[i].maskChar)
1144 if (!isValidInput(str.at(i), m_maskData[i].maskChar))
1152
1153
1154
1155
1156
1157
1158
1159QString QWidgetLineControl::maskString(
int pos,
const QString &str,
bool clear)
const
1161 if (pos >= m_maxLength)
1162 return QString::fromLatin1(
"");
1165 fill = clear ? clearString(0, m_maxLength) : m_text;
1168 QString s = QString::fromLatin1(
"");
1170 while (i < m_maxLength) {
1171 if (strIndex < str.size()) {
1172 if (m_maskData[i].separator) {
1173 s += m_maskData[i].maskChar;
1174 if (str[strIndex] == m_maskData[i].maskChar)
1178 if (isValidInput(str[strIndex], m_maskData[i].maskChar)) {
1179 switch (m_maskData[i].caseMode) {
1180 case MaskInputData::Upper:
1181 s += str[strIndex].toUpper();
1183 case MaskInputData::Lower:
1184 s += str[strIndex].toLower();
1192 int n = findInMask(i,
true,
true, str[strIndex]);
1194 if (str.size() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[strIndex]))) {
1195 s += QStringView{fill}.mid(i, n - i + 1);
1200 n = findInMask(i,
true,
false, str[strIndex]);
1202 s += QStringView{fill}.mid(i, n - i);
1203 switch (m_maskData[n].caseMode) {
1204 case MaskInputData::Upper:
1205 s += str[strIndex].toUpper();
1207 case MaskInputData::Lower:
1208 s += str[strIndex].toLower();
1229
1230
1231
1232
1233
1234QString QWidgetLineControl::clearString(
int pos,
int len)
const
1236 if (pos >= m_maxLength)
1240 int end = qMin(m_maxLength, pos + len);
1241 for (
int i = pos; i < end; ++i)
1242 if (m_maskData[i].separator)
1243 s += m_maskData[i].maskChar;
1251
1252
1253
1254
1255
1256QString QWidgetLineControl::stripString(
const QString &str)
const
1262 int end = qMin(m_maxLength, (
int)str.size());
1263 for (
int i = 0; i < end; ++i)
1264 if (m_maskData[i].separator)
1265 s += m_maskData[i].maskChar;
1267 if (str[i] != m_blank)
1274
1275
1276
1277int QWidgetLineControl::findInMask(
int pos,
bool forward,
bool findSeparator, QChar searchChar)
const
1279 if (pos >= m_maxLength || pos < 0)
1282 int end = forward ? m_maxLength : -1;
1283 int step = forward ? 1 : -1;
1287 if (findSeparator) {
1288 if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
1291 if (!m_maskData[i].separator) {
1292 if (searchChar.isNull())
1294 else if (isValidInput(searchChar, m_maskData[i].maskChar))
1303void QWidgetLineControl::internalUndo(
int until)
1305 if (!isUndoAvailable())
1307 cancelPasswordEchoTimer();
1310 while (m_undoState && m_undoState > until) {
1311 Command& cmd = m_history[--m_undoState];
1314 m_text.remove(cmd.pos, 1);
1318 m_selstart = cmd.selStart;
1319 m_selend = cmd.selEnd;
1323 case RemoveSelection:
1324 m_text.insert(cmd.pos, cmd.uc);
1325 m_cursor = cmd.pos + 1;
1328 case DeleteSelection:
1329 m_text.insert(cmd.pos, cmd.uc);
1335 if (until < 0 && m_undoState) {
1336 Command& next = m_history[m_undoState-1];
1337 if (next.type != cmd.type && next.type < RemoveSelection
1338 && (cmd.type < RemoveSelection || next.type == Separator))
1343 emitCursorPositionChanged();
1346void QWidgetLineControl::internalRedo()
1348 if (!isRedoAvailable())
1351 while (m_undoState < (
int)m_history.size()) {
1352 Command& cmd = m_history[m_undoState++];
1355 m_text.insert(cmd.pos, cmd.uc);
1356 m_cursor = cmd.pos + 1;
1359 m_selstart = cmd.selStart;
1360 m_selend = cmd.selEnd;
1365 case RemoveSelection:
1366 case DeleteSelection:
1367 m_text.remove(cmd.pos, 1);
1368 m_selstart = cmd.selStart;
1369 m_selend = cmd.selEnd;
1373 m_selstart = cmd.selStart;
1374 m_selend = cmd.selEnd;
1378 if (m_undoState < (
int)m_history.size()) {
1379 Command& next = m_history[m_undoState];
1380 if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
1381 && (next.type < RemoveSelection || cmd.type == Separator))
1386 emitCursorPositionChanged();
1390
1391
1392
1393
1394
1395void QWidgetLineControl::emitCursorPositionChanged()
1397 if (m_cursor != m_lastCursorPos) {
1398 const int oldLast = m_lastCursorPos;
1399 m_lastCursorPos = m_cursor;
1400 emit cursorPositionChanged(oldLast, m_cursor);
1401#if QT_CONFIG(accessibility)
1403 if (!hasSelectedText()) {
1404 QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
1405 QAccessible::updateAccessibility(&event);
1411#if QT_CONFIG(completer)
1414bool QWidgetLineControl::advanceToEnabledItem(
int dir)
1416 int start = m_completer->currentRow();
1419 int i = start + dir;
1420 if (dir == 0) dir = 1;
1422 if (!m_completer->setCurrentRow(i)) {
1423 if (!m_completer->wrapAround())
1425 i = i > 0 ? 0 : m_completer->completionCount() - 1;
1427 QModelIndex currentIndex = m_completer->currentIndex();
1428 if (m_completer->completionModel()->flags(currentIndex) & Qt::ItemIsEnabled)
1432 }
while (i != start);
1434 m_completer->setCurrentRow(start);
1438void QWidgetLineControl::complete(
int key)
1440 if (!m_completer || isReadOnly() || echoMode() != QLineEdit::Normal)
1443 QString text =
this->text();
1444 if (m_completer->completionMode() == QCompleter::InlineCompletion) {
1445 if (key == Qt::Key_Backspace)
1448 if (key == Qt::Key_Up || key == Qt::Key_Down) {
1449 if (textAfterSelection().size())
1451 QString prefix = hasSelectedText() ? textBeforeSelection()
1453 if (text.compare(m_completer->currentCompletion(), m_completer->caseSensitivity()) != 0
1454 || prefix.compare(m_completer->completionPrefix(), m_completer->caseSensitivity()) != 0) {
1455 m_completer->setCompletionPrefix(prefix);
1457 n = (key == Qt::Key_Up) ? -1 : +1;
1460 m_completer->setCompletionPrefix(text);
1462 if (!advanceToEnabledItem(n))
1465#ifndef QT_KEYPAD_NAVIGATION
1466 if (text.isEmpty()) {
1467 if (
auto *popup = QCompleterPrivate::get(m_completer)->popup)
1472 m_completer->setCompletionPrefix(text);
1475 m_completer->complete();
1479void QWidgetLineControl::setReadOnly(
bool enable)
1481 if (m_readOnly == enable)
1484 m_readOnly = enable;
1485 updateCursorBlinking();
1488void QWidgetLineControl::setBlinkingCursorEnabled(
bool enable)
1490 if (m_blinkEnabled == enable)
1493 m_blinkEnabled = enable;
1496 connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged,
this, &QWidgetLineControl::updateCursorBlinking);
1498 disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged,
this, &QWidgetLineControl::updateCursorBlinking);
1500 updateCursorBlinking();
1503void QWidgetLineControl::updateCursorBlinking()
1505 m_blinkTimer.stop();
1507 if (m_blinkEnabled && !m_readOnly) {
1508 const auto flashTime = QGuiApplication::styleHints()->cursorFlashTime() * 1ms;
1509 if (flashTime >= 2ms)
1510 m_blinkTimer.start(flashTime / 2,
this);
1514 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1518void QWidgetLineControl::resetCursorBlinkTimer()
1520 if (!m_blinkEnabled || !m_blinkTimer.isActive())
1522 m_blinkTimer.stop();
1523 const auto flashTime = QGuiApplication::styleHints()->cursorFlashTime() * 1ms;
1524 if (flashTime >= 2ms)
1525 m_blinkTimer.start(flashTime / 2,
this);
1529void QWidgetLineControl::timerEvent(QTimerEvent *event)
1531 const auto eventId = event->id();
1532 if (eventId == m_blinkTimer.id()) {
1533 m_blinkStatus = !m_blinkStatus;
1534 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1535 }
else if (eventId == m_deleteAllTimer.id()) {
1536 m_deleteAllTimer.stop();
1538 }
else if (eventId == m_tripleClickTimer.id()) {
1539 m_tripleClickTimer.stop();
1540 }
else if (eventId == m_passwordEchoTimer.id()) {
1541 m_passwordEchoTimer.stop();
1542 updateDisplayText();
1546#ifndef QT_NO_SHORTCUT
1547void QWidgetLineControl::processShortcutOverrideEvent(QKeyEvent *ke)
1549 if (ke == QKeySequence::Copy
1550 || ke == QKeySequence::MoveToNextWord
1551 || ke == QKeySequence::MoveToPreviousWord
1552 || ke == QKeySequence::MoveToStartOfLine
1553 || ke == QKeySequence::MoveToEndOfLine
1554 || ke == QKeySequence::MoveToStartOfDocument
1555 || ke == QKeySequence::MoveToEndOfDocument
1556 || ke == QKeySequence::SelectNextWord
1557 || ke == QKeySequence::SelectPreviousWord
1558 || ke == QKeySequence::SelectStartOfLine
1559 || ke == QKeySequence::SelectEndOfLine
1560 || ke == QKeySequence::SelectStartOfBlock
1561 || ke == QKeySequence::SelectEndOfBlock
1562 || ke == QKeySequence::SelectStartOfDocument
1563 || ke == QKeySequence::SelectAll
1564 || ke == QKeySequence::SelectEndOfDocument) {
1566 }
else if (ke == QKeySequence::Paste
1567 || ke == QKeySequence::Cut
1568 || ke == QKeySequence::Redo
1569 || ke == QKeySequence::Undo
1570 || ke == QKeySequence::DeleteCompleteLine) {
1573 }
else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1574 || ke->modifiers() == Qt::KeypadModifier) {
1575 if (ke->key() < Qt::Key_Escape) {
1579 switch (ke->key()) {
1580 case Qt::Key_Delete:
1581 case Qt::Key_Backspace:
1601void QWidgetLineControl::processKeyEvent(QKeyEvent* event)
1603 bool inlineCompletionAccepted =
false;
1605#if QT_CONFIG(completer)
1607 QCompleter::CompletionMode completionMode = m_completer->completionMode();
1608 auto *popup = QCompleterPrivate::get(m_completer)->popup;
1609 if ((completionMode == QCompleter::PopupCompletion
1610 || completionMode == QCompleter::UnfilteredPopupCompletion)
1611 && popup && popup->isVisible()) {
1614 switch (event->key()) {
1615 case Qt::Key_Escape:
1621 }
else if (completionMode == QCompleter::InlineCompletion) {
1622 switch (event->key()) {
1624 case Qt::Key_Return:
1626#ifdef QT_KEYPAD_NAVIGATION
1627 case Qt::Key_Select:
1628 if (!QApplicationPrivate::keypadNavigationEnabled())
1631 if (!m_completer->currentCompletion().isEmpty() && hasSelectedText()
1632 && !m_completer->completionPrefix().isEmpty()
1633 && textAfterSelection().isEmpty()) {
1634 setText(m_completer->currentCompletion());
1635 inlineCompletionAccepted =
true;
1645 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
1646 if (hasAcceptableInput() || fixup()) {
1648 QInputMethod *inputMethod = QGuiApplication::inputMethod();
1649 inputMethod->commit();
1650 QWidget *lineEdit = qobject_cast<QWidget *>(parent());
1651 if (!(lineEdit && lineEdit->inputMethodHints() & Qt::ImhMultiLine))
1652 inputMethod->hide();
1655 emit editingFinished();
1657 if (inlineCompletionAccepted)
1664 if (echoMode() == QLineEdit::PasswordEchoOnEdit
1665 && !passwordEchoEditing()
1667 && !event->text().isEmpty()
1668#ifdef QT_KEYPAD_NAVIGATION
1669 && event->key() != Qt::Key_Select
1670 && event->key() != Qt::Key_Up
1671 && event->key() != Qt::Key_Down
1672 && event->key() != Qt::Key_Back
1674 && !(event->modifiers() & Qt::ControlModifier)) {
1680 updatePasswordEchoEditing(
true);
1684 bool unknown =
false;
1685#if QT_CONFIG(shortcut)
1686 bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
1691#ifndef QT_NO_SHORTCUT
1692 else if (event == QKeySequence::Undo) {
1696 else if (event == QKeySequence::Redo) {
1700 else if (event == QKeySequence::SelectAll) {
1703#ifndef QT_NO_CLIPBOARD
1704 else if (event == QKeySequence::Copy) {
1707 else if (event == QKeySequence::Paste) {
1708 if (!isReadOnly()) {
1709 QClipboard::Mode mode = QClipboard::Clipboard;
1710 if (m_keyboardScheme == QPlatformTheme::X11KeyboardScheme
1711 && event->modifiers() == (Qt::CTRL | Qt::SHIFT)
1712 && event->key() == Qt::Key_Insert) {
1713 mode = QClipboard::Selection;
1718 else if (event == QKeySequence::Cut) {
1719 if (!isReadOnly() && hasSelectedText()) {
1724 else if (event == QKeySequence::DeleteEndOfLine) {
1725 if (!isReadOnly()) {
1726 setSelection(cursor(), end());
1732 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
1735 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
1738 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
1741 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
1744 else if (event == QKeySequence::MoveToNextChar) {
1745#if !QT_CONFIG(completer)
1746 const bool inlineCompletion =
false;
1748 const bool inlineCompletion = m_completer && m_completer->completionMode() == QCompleter::InlineCompletion;
1750 if (hasSelectedText()
1751 && (m_keyboardScheme != QPlatformTheme::WindowsKeyboardScheme
1752 || inlineCompletion)) {
1753 moveCursor(selectionEnd(),
false);
1755 cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1758 else if (event == QKeySequence::SelectNextChar) {
1759 cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1761 else if (event == QKeySequence::MoveToPreviousChar) {
1762#if !QT_CONFIG(completer)
1763 const bool inlineCompletion =
false;
1765 const bool inlineCompletion = m_completer && m_completer->completionMode() == QCompleter::InlineCompletion;
1767 if (hasSelectedText()
1768 && (m_keyboardScheme != QPlatformTheme::WindowsKeyboardScheme
1769 || inlineCompletion)) {
1770 moveCursor(selectionStart(),
false);
1772 cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1775 else if (event == QKeySequence::SelectPreviousChar) {
1776 cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1778 else if (event == QKeySequence::MoveToNextWord) {
1779 if (echoMode() == QLineEdit::Normal)
1780 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
1782 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
1784 else if (event == QKeySequence::MoveToPreviousWord) {
1785 if (echoMode() == QLineEdit::Normal)
1786 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
1787 else if (!isReadOnly()) {
1788 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
1791 else if (event == QKeySequence::SelectNextWord) {
1792 if (echoMode() == QLineEdit::Normal)
1793 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
1795 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
1797 else if (event == QKeySequence::SelectPreviousWord) {
1798 if (echoMode() == QLineEdit::Normal)
1799 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
1801 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
1803 else if (event == QKeySequence::Delete) {
1807 else if (event == QKeySequence::DeleteEndOfWord) {
1808 if (!isReadOnly()) {
1809 if (!hasSelectedText())
1810 cursorWordForward(
true);
1812 if (hasSelectedText())
1816 else if (event == QKeySequence::DeleteStartOfWord) {
1817 if (!isReadOnly()) {
1818 if (!hasSelectedText())
1819 cursorWordBackward(
true);
1821 if (hasSelectedText())
1824 }
else if (event == QKeySequence::DeleteCompleteLine) {
1825 if (!isReadOnly()) {
1826 setSelection(0, text().size());
1827#ifndef QT_NO_CLIPBOARD
1835 bool handled =
false;
1836 if (m_keyboardScheme == QPlatformTheme::MacKeyboardScheme
1837 && (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)) {
1838 Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier);
1839 if (myModifiers & Qt::ShiftModifier) {
1840 if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier)
1841 || myModifiers == (Qt::AltModifier|Qt::ShiftModifier)
1842 || myModifiers == Qt::ShiftModifier) {
1844 event->key() == Qt::Key_Up ? home(1) : end(1);
1847 if ((myModifiers == Qt::ControlModifier
1848 || myModifiers == Qt::AltModifier
1849 || myModifiers == Qt::NoModifier)) {
1850 event->key() == Qt::Key_Up ? home(0) : end(0);
1855 if (event->modifiers() & Qt::ControlModifier) {
1856 switch (event->key()) {
1857 case Qt::Key_Backspace:
1858 if (!isReadOnly()) {
1859 cursorWordBackward(
true);
1863#if QT_CONFIG(completer)
1866 complete(event->key());
1874 switch (event->key()) {
1875 case Qt::Key_Backspace:
1876 if (!isReadOnly()) {
1878#if QT_CONFIG(completer)
1879 complete(Qt::Key_Backspace);
1883#ifdef QT_KEYPAD_NAVIGATION
1885 if (QApplicationPrivate::keypadNavigationEnabled() && !event->isAutoRepeat()
1887 if (text().length() == 0) {
1888 setText(m_cancelText);
1890 if (passwordEchoEditing())
1891 updatePasswordEchoEditing(
false);
1893 emit editFocusChange(
false);
1894 }
else if (!m_deleteAllTimer.isActive()) {
1895 m_deleteAllTimer.start(750ms,
this);
1909 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
1910 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1916 && isAcceptableInput(event)) {
1917 insert(event->text());
1918#if QT_CONFIG(completer)
1919 complete(event->key());
1928#ifndef QT_NO_CLIPBOARD
1929 if (QApplication::clipboard()->supportsSelection())
1930 copy(QClipboard::Selection);
1936bool QWidgetLineControl::isUndoAvailable()
const
1940 return !m_readOnly && m_undoState
1941 && (m_echoMode == QLineEdit::Normal || m_history[m_undoState - 1].type == QWidgetLineControl::Insert);
1944bool QWidgetLineControl::isRedoAvailable()
const
1948 && m_echoMode == QLineEdit::Normal
1949 && m_undoState <
int(m_history.size());
1954#include "moc_qwidgetlinecontrol_p.cpp"